手把手教你写一个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);
这里漏了一个map<string, field> columnfieldmap = entityutils.columnfieldmap(clz);的代码,在下面补上:
/** * 获取table的列名与entity属性的映射map * * @param clz * @param <t> * @return */ public static <t> map<string, field> columnfieldmap(class<t> clz) { field[] declaredfields = clz.getdeclaredfields(); map<string, field> map = new hashmap<>(declaredfields.length); for (field field : declaredfields) { if (iscolumn(field)) { map.put(columnname(field), field); } } return map; }
这时候,解析class里面的工作就完成了,下一步就是要通过拿到的数据来拼装sql了。
我下一篇再写^_^~
上一篇: 谁敢超过我们就把谁带走
下一篇: 锅里狗肉