51Testing软件测试论坛

标题: 手把手教你写一个java的orm(三) [打印本页]

作者: 草帽路飞UU    时间: 2019-2-1 16:20
标题: 手把手教你写一个java的orm(三)
使用反射解析class
上一篇我们完成了class到表映射关系的建立,但是这个并不能被代码正确处理,我们还需要让程序能够正确的识别这些映射关系。
这一篇主要讲的是建立一个从class到表的模型,使我们在class上添加的注解能够正确的被识别并处理。这里主要用到的是java中的反射相关的知识。不了解的同学请自行百度一下,不是很难~,另外这一篇也会稍微的提到一点反射的用法。
现在开始。
我们主要的需求是根绝我们添加的注解,生成各种类型的sql语句,所以我们首先要能够获取添加在java类名,属性,方法上的注解,并获取注解中的值。所以第一步:
获取特定的注解
这里我们获取注解的方法就写完了,可以通过这些方法获取我们需要的注解,并通过获取到的注解拿到其中的值。
大致是这样的:(这里的User.class)可以看 上一篇。
//代码(获取class上的注解)@Testpublic void getClassAnnotation() {    Table annotation = EntityUtils.getAnnotation(User.class, Table.class);    System.out.println(annotation.name());}//输出结果user//代码(获取field上的注解)@Testpublic 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的,不包括父类)后,循环遍历一遍,根据每个属性上不同的注解加以区分就好了。这里为了简单,我定义了几个方法:
这几个方法的具体代码我就不贴出来了,很简单的。下面是取出一个class中所有属性的代码:
for (Field field : clz.getDeclaredFields()) {    if (isColumn(field)) {        //执行需要的操作。    }}
在这个遍历个过程中我们可以新建一个类,里面用来存放表和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了。
我下一篇再写^_^~

作者: qqq911    时间: 2019-4-16 11:17
感谢分享
作者: Miss_love    时间: 2020-12-25 15:55
感谢分享




欢迎光临 51Testing软件测试论坛 (http://bbs.51testing.com/) Powered by Discuz! X3.2