手把手教你写一个java的orm(三)
使用反射解析class
上一篇我们完成了class到表映射关系的建立,但是这个并不能被代码正确处理,我们还需要让程序能够正确的识别这些映射关系。
这一篇主要讲的是建立一个从class到表的模型,使我们在class上添加的注解能够正确的被识别并处理。这里主要用到的是java中的反射相关的知识。不了解的同学请自行百度一下,不是很难~,另外这一篇也会稍微的提到一点反射的用法。
现在开始。
我们主要的需求是根绝我们添加的注解,生成各种类型的sql语句,所以我们首先要能够获取添加在java类名,属性,方法上的注解,并获取注解中的值。所以第一步:
获取特定的注解
获取class上的注解
在Java的Class中提供了.getAnnotation(annotationClass)的方法,
这里我在这个方法的基础上包了一层,主要是使用断言做了一些验证,在验证不通过的时候抛出我认识的异常信息。下面的方法都是如此处理的。
/** * 获取类上的注解 * * @param clz * @param annotationClass * @param <T> * @return */ public static <T extends Annotation> T getAnnotation(Class<?> clz, Class<T> annotationClass) { Assert.notNull(clz, CLASS_NOT_NULL); Assert.notNull(annotationClass, ANNOTATIONCLASS_NOT_NULL); return clz.getAnnotation(annotationClass); }
获取属性上的注解
/** * 获取属性上的注解 * * @param field * @param annotationClass * @param <T> * @return */ public static <T extends Annotation> T getAnnotation(Field field, Class<T> annotationClass) { Assert.notNull(field, FIELD_NOT_NULL); Assert.notNull(annotationClass, ANNOTATIONCLASS_NOT_NULL); return field.getAnnotation(annotationClass); }
这里我们获取注解的方法就写完了,可以通过这些方法获取我们需要的注解,并通过获取到的注解拿到其中的值。
大致是这样的:(这里的User.class)可以看 上一篇。
//代码(获取class上的注解) @Test public void getClassAnnotation() { Table annotation = EntityUtils.getAnnotation(User.class, Table.class); System.out.println(annotation.name()); } //输出结果 user //代码(获取field上的注解) @Test public void getFieldAnnotation() throws NoSuchFieldException { Class userClass = User.class; //getDeclaredField和getField是有一定区别的,这里用getDeclaredField Field field = userClass.getDeclaredField("createDate"); Column annotation = EntityUtils.getAnnotation(field, Column.class); System.out.println(annotation.name()); } //输出结果 create_date
这样就可以获取到我们在class上添加的注解,以及注解中的值了。下面是第二步
获取Id以及Column
这里依然是通过反射实现,主要就是获取到这个class中的所有的属性(只属于这个class的,不包括父类)后,循环遍历一遍,根据每个属性上不同的注解加以区分就好了。这里为了简单,我定义了几个方法:
boolean isTable(Class aClass)
是不是添加了@Table
boolean isColumn(Field field)
是不是添加了@Column
boolean isId(Field field)
是不是添加了@Id
这几个方法的具体代码我就不贴出来了,很简单的。下面是取出一个class中所有属性的代码:
for (Field field : clz.getDeclaredFields()) { if (isColumn(field)) { //执行需要的操作。 } }
在这个遍历个过程中我们可以新建一个类,里面用来存放表和class的各种对应关系。比如:
- class属性名称与表字段名称的对应。
- 表中的id是class中哪一个属性。
- 表的字段名称对应的是class里的那个个属性。
- 等等~~
代码大致是这样的 EntityTableRowMapper.java:
/** * id的字段名称 */ private String idName = null; /** * table对应的class */ private Class<T> tableClass = null; /** * 对应的数据库名称 */ private String tableName = null; /** * 表中所有的字段 */ private Set<String> columnNames = null; /** * 表中所有的字段对应的属性名称 */ private Set<String> fieldNames = null; /** * 属性名称和数据库字段名的映射 * K: 属性名 * V:表字段名称 */ private Map<String, String> fieldNameColumnMapper = null; /** * 数据库字段名和class属性的映射 * K:表字段名称 * V:class属性 */ private Map<String, Field> columnFieldMapper = null;
这些用来描述表和class之间的关系就已经够用了。只要按照关系将里面的数据一一填充完毕就好。我写了一个方法来填充这些数据,代码是这样的:
//这里只是示例 Class clz = User.class(); //这里是主要代码 EntityTableRowMapper mapper = new EntityTableRowMapper(); Map<String, Field> columnFieldMap = EntityUtils.columnFieldMap(clz); int size = columnFieldMap.size(); Map<String, String> fieldNameColumnMapper = new HashMap<>(size); Set<String> columnNames = new HashSet<>(size); Set<String> fieldNames = new HashSet<>(size); mapper.setTableClass(clz); mapper.setTableName(EntityUtils.tableName(clz)); mapper.setIdName(EntityUtils.idColumnName(clz)); mapper.setColumnFieldMapper(columnFieldMap); for (Map.Entry<String, Field> entry : columnFieldMap.entrySet()) { String columnName = entry.getKey(); Field field = entry.getValue(); String fieldName = field.getName(); fieldNameColumnMapper.put(fieldName, columnName); fieldNames.add(fieldName); columnNames.add(columnName); } mapper.setColumnNames(columnNames); mapper.setFieldNameColumnMapper(fieldNameColumnMapper); mapper.setFieldNames(fieldNames);
这时候,解析class里面的工作就完成了,下一步就是要通过拿到的数据来拼装sql了。
我下一篇再写^_^~- 手把手教你写一个ORM(二)
- [JAVA]打造一个缩水版的ORM映射(mih)-- beta
- 开源组件:(4)用元数据和BeanUtils写一个简单的ORM映射BaseDAO.java
- 手把手教你写一个Java在线聊天系统
- sql2java:一个古老但稳定的轻量级的ORM工具的使用说明
- 一个Java线程小例子(仿火车票售卖)
- java判断一个字符串是否是回文
- Java靠强大的JVM维护了一个蓝领级语言?
- java用write()拷贝一个文本文件
- JSF历程一个程序员的JavaWeb之路
- 找一个能做事的Java程序员怎么就这么难
- 使用Eclipse发布一个依赖于其他项目的java项目,被依赖的项目不能自动编译,因而引发notfoundClass的异常。
- Java实现:输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果。
- 刚看了一个程序,并写了一下,然后用Java 再写了一下
- java绘图的基本原理:画一个圆
- java 截取一个包含汉字的字符串的前n个字节的算法
- 回调--一个经典例子让你彻彻底底理解java回调机制
- 每日一省之 ———— 一个简单的跳跃表(Skip List)的Java实现
- 一个简单的Java应用程序
- 一个隐形的java int溢出