1. package com.tofflon.mes.admin.utils;
    2. import com.baomidou.mybatisplus.annotation.TableField;
    3. import com.baomidou.mybatisplus.annotation.TableName;
    4. import io.swagger.annotations.ApiModel;
    5. import io.swagger.annotations.ApiModelProperty;
    6. import lombok.extern.slf4j.Slf4j;
    7. import org.apache.commons.lang3.StringUtils;
    8. import org.springframework.beans.factory.annotation.Autowired;
    9. import org.springframework.core.io.Resource;
    10. import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
    11. import org.springframework.core.io.support.ResourcePatternResolver;
    12. import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
    13. import org.springframework.core.type.classreading.MetadataReader;
    14. import org.springframework.core.type.classreading.MetadataReaderFactory;
    15. import org.springframework.stereotype.Component;
    16. import org.springframework.util.ClassUtils;
    17. import org.springframework.util.CollectionUtils;
    18. import javax.annotation.PostConstruct;
    19. import java.lang.reflect.Field;
    20. import java.util.*;
    21. import java.util.regex.Matcher;
    22. import java.util.regex.Pattern;
    23. /**
    24. * @author X-MD
    25. * @date 2021/1/20 0020
    26. * @description: 自动更新字段
    27. **/
    28. @Slf4j
    29. @Component
    30. public class BeanUtils {
    31. private final static Pattern HUMP_PATTERN = Pattern.compile("[A-Z]");
    32. /**
    33. * 实体类路径
    34. */
    35. private final String[] BASE_PACKAGE = {"com.xxxx.mes.admin.api.entity"};
    36. private final String RESOURCE_PATTERN = "/**/*.class";
    37. //表名前缀
    38. @Autowired
    39. protected BaseModelMapper baseModelMapper;
    40. @PostConstruct
    41. public void init() {
    42. try {
    43. Set<Class<?>> modelSet = new HashSet<>();
    44. for (String spath : BASE_PACKAGE) {
    45. ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
    46. String pattern = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + ClassUtils.convertClassNameToResourcePath(spath) + RESOURCE_PATTERN;
    47. Resource[] resources = resourcePatternResolver.getResources(pattern);
    48. //MetadataReader 的工厂类
    49. MetadataReaderFactory readerfactory = new CachingMetadataReaderFactory(resourcePatternResolver);
    50. for (Resource resource : resources) {
    51. //用于读取类信息
    52. MetadataReader reader = readerfactory.getMetadataReader(resource);
    53. //扫描到的class
    54. String classname = reader.getClassMetadata().getClassName();
    55. Class<?> clazz = Class.forName(classname);
    56. //将对应的类存在list
    57. modelSet.add(clazz);
    58. }
    59. }
    60. for (Class<?> aClass : modelSet) {
    61. String tableComment = StringUtils.EMPTY;
    62. //根据ApiModel获取表注释
    63. ApiModel apiModel = aClass.getAnnotation(ApiModel.class);
    64. if (apiModel != null) {
    65. tableComment = apiModel.value();
    66. }
    67. //根据TableName获取表名称
    68. TableName tableNameAnn = aClass.getAnnotation(TableName.class);
    69. String tableName;
    70. //有的实体类别没有通过TableName竹节布指定表明,就是通过类名转下划线的形式
    71. if (tableNameAnn == null) {
    72. String simpleName = aClass.getSimpleName();
    73. //过滤掉不是数据库映射的类
    74. if ("BaseEntity".equals(simpleName)) {
    75. continue;
    76. }
    77. tableName = StringUtils.strip(humpToLine2(simpleName), "_");
    78. } else {
    79. tableName = tableNameAnn.value();
    80. }
    81. //校测表是否存在,返回表的所有字段
    82. List<String> columnTable = baseModelMapper.columnTable(tableName);
    83. //本类的所有字段
    84. Field[] declaredFields = aClass.getDeclaredFields();
    85. //父类所有字段
    86. Field[] supercFields = aClass.getSuperclass().getDeclaredFields();
    87. //数组合并
    88. Field[] fields = new Field[declaredFields.length + supercFields.length];
    89. System.arraycopy(declaredFields, 0, fields, 0, declaredFields.length);
    90. System.arraycopy(supercFields, 0, fields, declaredFields.length, supercFields.length);
    91. //字段集合
    92. List<String> fieldList = new ArrayList<>();
    93. //字段备注信息集合
    94. Map<String, String> fieldRemarkMap = new HashMap<>(16);
    95. //字段数据类型信息集合
    96. Map<String, String> dataTypeMap = new HashMap<>(16);
    97. for (Field declaredField : fields) {
    98. //读取字段上ApiModelProperty注解的值
    99. ApiModelProperty annotation = declaredField.getAnnotation(ApiModelProperty.class);
    100. if (annotation == null) {
    101. continue;
    102. }
    103. //读取字段上TableField注解的值
    104. TableField tableField = declaredField.getAnnotation(TableField.class);
    105. //去除无用字段
    106. boolean serial = "serialVersionUID".equalsIgnoreCase(declaredField.getName());
    107. //如果存在且exist值为false,说明该字段不与数据映射
    108. boolean bool = tableField != null && !tableField.exist();
    109. if (bool || serial) {
    110. continue;
    111. }
    112. //字段名称
    113. String fieldName = humpToLine2(declaredField.getName());
    114. fieldList.add(fieldName);
    115. //字段备注
    116. String value = annotation.value();
    117. fieldRemarkMap.put(fieldName, value);
    118. //添加字段时最好在注解里备注好
    119. String dataType = annotation.dataType();
    120. if (StringUtils.isBlank(dataType)) {
    121. dataType = declaredField.getType().getName();
    122. }
    123. dataTypeMap.put(fieldName, dataType);
    124. }
    125. //如果表不存在,添加表
    126. if (CollectionUtils.isEmpty(columnTable)) {
    127. //tableName 表明 ;;tableComment 表备注信息
    128. baseModelMapper.createTable(tableName, tableComment);
    129. }
    130. for (String fieldName : fieldList) {
    131. //与数据库字段比较是否存在,如果存才不操作,不存在就添加
    132. boolean match = columnTable.stream().anyMatch(s -> s.equalsIgnoreCase(fieldName));
    133. if (!match) {
    134. //字段数据类型
    135. String dataType = dataTypeMap.get(fieldName);
    136. String fieldadd = propertyType(dataType, fieldName);
    137. //备注信息
    138. String comment = fieldRemarkMap.get(fieldName);
    139. alterTable(tableName, fieldadd, comment);
    140. }
    141. }
    142. }
    143. } catch (Exception e) {
    144. log.error(e.getMessage());
    145. }
    146. }
    147. /**
    148. * 更新字段
    149. *
    150. * @param tableName 表名称
    151. * @param fieldadd 更新字段
    152. * @param comment 字段备注
    153. */
    154. public void alterTable(String tableName, String fieldadd, String comment) {
    155. try {
    156. //更新字段
    157. baseModelMapper.alterTable(tableName, fieldadd, comment);
    158. } catch (Exception e) {
    159. log.error(e.getMessage());
    160. }
    161. }
    162. /**
    163. * @param type 数据类型
    164. * @param name 表名称
    165. * @return
    166. */
    167. private static String propertyType(String type, String name) {
    168. name = humpToLine2(name);
    169. if ("java.util.Date".equals(type)) {
    170. return name + StringUtils.SPACE + "datetime(0)";
    171. } else if ("java.lang.String".equals(type)) {
    172. return name + StringUtils.SPACE + "varchar(255)";
    173. } else if ("java.lang.Integer".equals(type)) {
    174. return name + StringUtils.SPACE + "int(11)";
    175. } else if ("java.math.BigDecimal".equals(type)) {
    176. return name + StringUtils.SPACE + "decimal(10,2)";
    177. } else if ("java.lang.Long".equals(type)) {
    178. return name + StringUtils.SPACE + "bigint(20)";
    179. } else if ("java.lang.Boolean".equals(type)) {
    180. return name + StringUtils.SPACE + "tinyint(2)";
    181. } else if ("java.time.LocalDateTime".equals(type)) {
    182. return name + StringUtils.SPACE + "datetime(0)";
    183. }
    184. return name + StringUtils.SPACE + type;
    185. }
    186. /**
    187. * 31 * 驼峰转下划线
    188. * 32
    189. */
    190. public static String humpToLine2(String str) {
    191. Matcher matcher = HUMP_PATTERN.matcher(str);
    192. StringBuffer sb = new StringBuffer();
    193. while (matcher.find()) {
    194. matcher.appendReplacement(sb, "_" + matcher.group(0).toLowerCase());
    195. }
    196. matcher.appendTail(sb);
    197. return sb.toString();
    198. }
    199. }