根據已有類註解做爲字段註釋,進行建表。

最近爬蟲項目須要根據返回的JSON結構建立相應的表,根據要求表字段必須添加儘可能完善的註解。緩存

一、一般一個JSON結構在70-250字段之間,要根據網頁表頭與JSON數據,對比出表字段註解;app

二、領域類每一個字段須要添加兩個和註解相關的註解;函數

三、數據源來自多種系統,並已進行了部分取數保存;工具

200多字段對比出一堆專業名詞枯燥機械,累。測試

所以建立一個小工具類,從已有類中讀取其註解中的字段註解部分,造成字典庫;而後遍歷JSON的字段信息進行註解添加,Model註解使用工具從表生成Java領域類,而後稍加改動便可。ui

主要代碼以下:編碼

反射工具:spa

  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     }

測試用例:code

 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     }
相關文章
相關標籤/搜索