根据已有类注解作为字段注释,进行建表。

最近爬虫项目需要根据返回的JSON结构创建相应的表,根据要求表字段必须添加尽量完善的注解。

1、通常一个JSON结构在70-250字段之间,要根据网页表头与JSON数据,对比出表字段注解;

2、领域类每个字段需要添加两个和注解相关的注解;

3、数据源来自多种系统,并已进行了部分取数保存;

200多字段对比出一堆专业名词枯燥机械,累。

因此创建一个小工具类,从已有类中读取其注解中的字段注解部分,形成字典库;然后遍历JSON的字段信息进行注解添加,Model注解使用工具从表生成Java领域类,然后稍加改动即可。

主要代码如下:

反射工具:

  1 private static final Field[] EMPTY_FIELD_ARRAY = new Field[0];
  2     private static final String[] EMPTY_CAMEL_LINE_FIELD_ARRAY = new String[0];
  3 
  4     /**
  5      * 缓存
  6      */
  7     private static final Map<Class<?>, Field[]> DECLARED_FIELDS_CACHE = new ConcurrentReferenceHashMap<>(256);
  8     private static final Map<Class<?>, String[]> DECLARED_CAMEL_LINE_FIELDS_CACHE = new ConcurrentReferenceHashMap<>(256);
  9 
 10     /**
 11      * 获取所有字段信息
 12      *
 13      * @param classType 类型
 14      * @return Field[]
 15      */
 16     public static Field[] getDeclaredFields(Class<?> classType) {
 17         Assert.notNull(classType, "Class must not be null");
 18         Field[] result = DECLARED_FIELDS_CACHE.get(classType);
 19         if (Objects.isNull(result)) {
 20             try {
 21                 result = classType.getDeclaredFields();
 22                 DECLARED_FIELDS_CACHE.put(classType, (result.length == 0 ? EMPTY_FIELD_ARRAY : result));
 23             } catch (Throwable ex) {
 24                 throw new IllegalStateException("Failed to introspect Class [" + classType.getName() +
 25                         "] from ClassLoader [" + classType.getClassLoader() + "]", ex);
 26             }
 27         }
 28         return result;
 29     }
 30 
 31     /**
 32      * 以CameLine字符串形式获取所有字段信息
 33      *
 34      * @param classType 类型
 35      * @return String[]
 36      */
 37     public static String[] getDeclaredCameLineFields(Class<?> classType) {
 38         Assert.notNull(classType, "Class must not be null");
 39         String[] result = DECLARED_CAMEL_LINE_FIELDS_CACHE.get(classType);
 40         if (Objects.isNull(result)) {
 41             Field[] fields = getDeclaredFields(classType);
 42             result = Arrays.stream(fields).map(field -> StringUtils.camelLine(field.getName())).toArray(String[]::new);
 43             DECLARED_CAMEL_LINE_FIELDS_CACHE.put(classType, (result.length == 0 ? EMPTY_CAMEL_LINE_FIELD_ARRAY : result));
 44         }
 45         return result;
 46     }
 47 
 48     /**
 49      * 获取属性的注解值
 50      *
 51      * @param field           属性
 52      * @param annotationClass 注解类型
 53      * @param function        转换函数
 54      * @param <T>             注解类型
 55      * @param <R>             值类型
 56      * @return Optional<R>
 57      */
 58     public static <T extends Annotation, R> Optional<R> getDeclaredFieldAnnotation(Field field, Class<T> annotationClass, Function<T, R> function) {
 59         if (field.isAnnotationPresent(annotationClass)) {
 60             return Optional.ofNullable(function.apply(field.getAnnotation(annotationClass)));
 61         }
 62         return Optional.empty();
 63     }
 64 
 65     /**
 66      * 获取类的字段 -> 注解值映射
 67      *
 68      * @param classType       类型
 69      * @param annotationClass 注解类型
 70      * @param function        转换函数
 71      * @param <T>             注解类型
 72      * @param <R>             值类型
 73      * @return Map<Field, R>
 74      */
 75     public static <T extends Annotation, R> Map<Field, R> getDeclaredFieldsAnnotation(Class<?> classType, Class<T> annotationClass, Function<T, R> function) {
 76         Field[] declaredFields = getDeclaredFields(classType);
 77         Map<Field, R> fieldAnnotationMap = new HashMap<>(14);
 78         for (Field field : declaredFields) {
 79             Optional<R> declaredFieldAnnotation = getDeclaredFieldAnnotation(field, annotationClass, function);
 80             if (declaredFieldAnnotation.isPresent()) {
 81                 R r = declaredFieldAnnotation.get();
 82                 fieldAnnotationMap.put(field, r);
 83             }
 84         }
 85         return fieldAnnotationMap;
 86     }
 87 
 88     /**
 89      * 获取一组类的字段 -> 注解值映射
 90      *
 91      * @param classTypeList   类型集合
 92      * @param annotationClass 注解类型
 93      * @param function        转换函数
 94      * @param <T>             注解类型
 95      * @param <R>             值类型
 96      * @return Map<Field, Set < R>>
 97      */
 98     public static <T extends Annotation, R> Map<Field, Set<R>> getAllClassDeclaredFieldAnnotations(List<Class<?>> classTypeList, Class<T> annotationClass, Function<T, R> function) {
 99         return classTypeList.stream()
100                 .flatMap(classType -> getDeclaredFieldsAnnotation(classType, annotationClass, function).entrySet().stream())
101                 .collect(Collectors.toMap(
102                         Map.Entry::getKey,
103                         entry -> {
104                             HashSet<R> rHashSet = new HashSet<>();
105                             rHashSet.add(entry.getValue());
106                             return rHashSet;
107                         },
108                         (k1, k2) -> {
109                             if (k1.size() > k2.size()) {
110                                 k1.addAll(k2);
111                                 return k1;
112                             } else {
113                                 k2.addAll(k1);
114                                 return k2;
115                             }
116                         }
117                 ));
118     }
119 
120     /**
121      * 将Field类型键转换为String类型
122      *
123      * @param fieldMap Map<String, Set<T>>
124      * @param <T>      泛型参数
125      * @return Map<String, Set < T>>
126      */
127     public static <T> Map<String, Set<T>> fieldToCameLineOfMap(Map<Field, Set<T>> fieldMap) {
128         return fieldMap.entrySet()
129                 .stream()
130                 .collect(Collectors.toMap(
131                         entry -> StringUtils.camelLine(entry.getKey().getName()),
132                         Map.Entry::getValue,
133                         // Map键本身不重复
134                         (k1, k2) -> k1
135                 ));
136     }
137 
138 /**
139      * 将驼峰字符串转换为带下划线的字符串,例如MyAccout,转换为my_account
140      *
141      * @param str 源字符串
142      * @return 转换后的字符串
143      */
144     public static String camelLine(String str) {
145         StringBuilder stringBuilder = new StringBuilder();
146         int index = 0;
147         for (int i = 1, length = str.length(); i < length; i++) {
148             if (Character.isUpperCase(str.charAt(i))) {
149                 stringBuilder.append(str, index, i).append("_");
150                 index = i;
151             }
152         }
153         return stringBuilder.append(str.substring(index)).toString().toLowerCase();
154     }

测试用例:

 1 @Test
 2     public void generateTableSQL() {
 3         String modelJsonStr = "";
 4         String tableName = "table_name";
 5         String tableComment = "table_comment";
 6 
 7         JSONObject modelJsonObject = JSONObject.parseObject(modelJsonStr);
 8         Set<Map.Entry<String, Object>> entrySet = modelJsonObject.entrySet();
 9 
10         Map<Field, Set<String>> allClassDeclaredFieldAnnotations = ReflectionUtils.getAllClassDeclaredFieldAnnotations(
11                 // 字典集
12                 Arrays.asList(
13                         // 选取**系统相关类
14                         
15                 ),
16                 ApiModelProperty.class,
17                 ApiModelProperty::value);
18         Map<String, Set<String>> dict = ReflectionUtils.fieldToCameLineOfMap(allClassDeclaredFieldAnnotations);
19         Set<String> dictKeys = dict.keySet();
20 
21         StringBuilder stringBuilder = new StringBuilder();
22         stringBuilder.append(String.format("CREATE TABLE %s (\n", tableName))
23                 .append("\tid varchar(36) NOT NULL COMMENT 'ID',\n")
24                 .append("\texport_date varchar(20) NULL COMMENT '导出时间',\n")
25                 .append("\tadd_time varchar(20) NULL COMMENT '添加时间',\n")
26                 .append("\tidx varchar(10) NULL COMMENT '序号',\n")
27                 .append("\tdept_id varchar(50) NULL COMMENT '组织机构编码',\n");
28 
29         Map<String, String> ignoreMap = new HashMap<>(14);
30         for (Map.Entry<String, Object> entry : entrySet) {
31             String key = entry.getKey();
32             stringBuilder.append("\t").append(key)
33                     .append(" varchar(100) NULL COMMENT '");
34             if (dictKeys.contains(key)) {
35                 // 可自定义字典查询规则
36                 stringBuilder.append(dict.get(key).stream().findFirst().orElse("").trim());
37             } else {
38                 ignoreMap.put(key, entry.getValue() == null ? "" : entry.getValue().toString());
39             }
40             stringBuilder.append("',\n");
41         }
42         stringBuilder.append("\tCONSTRAINT ").append(tableName).append(" PRIMARY KEY (id)\n")
43                 .append(")\n")
44                 .append("ENGINE=InnoDB\n")
45                 .append("DEFAULT CHARSET=utf8\n")
46                 .append("COLLATE=utf8_general_ci\n")
47                 .append("COMMENT='").append(tableComment).append("';");
48         // 不负责最终SQL的格式化
49         System.out.println(stringBuilder.toString());
50         System.out.printf("字典未包含字段:%d个\n%s\n", ignoreMap.size(), ignoreMap.entrySet().stream().map(entry -> String.format("\t\"%s\": \"%s\"", entry.getKey(), entry.getValue())).collect(Collectors.joining(",\n", "{\n", "\n}")));
51     }

 

posted @ 2021-01-11 21:40  河图书卦  阅读(152)  评论(0编辑  收藏  举报