使用反射解析 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 了.
我下一篇再写 ^_^~
来源: https://www.cnblogs.com/hebaibai/p/10299265.html