package com.tofflon.mes.admin.utils;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.stereotype.Component;
import org.springframework.util.ClassUtils;
import org.springframework.util.CollectionUtils;
import javax.annotation.PostConstruct;
import java.lang.reflect.Field;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* @author X-MD
* @date 2021/1/20 0020
* @description: 自动更新字段
**/
@Slf4j
@Component
public class BeanUtils {
private final static Pattern HUMP_PATTERN = Pattern.compile("[A-Z]");
/**
* 实体类路径
*/
private final String[] BASE_PACKAGE = {"com.xxxx.mes.admin.api.entity"};
private final String RESOURCE_PATTERN = "/**/*.class";
//表名前缀
@Autowired
protected BaseModelMapper baseModelMapper;
@PostConstruct
public void init() {
try {
Set<Class<?>> modelSet = new HashSet<>();
for (String spath : BASE_PACKAGE) {
ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
String pattern = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + ClassUtils.convertClassNameToResourcePath(spath) + RESOURCE_PATTERN;
Resource[] resources = resourcePatternResolver.getResources(pattern);
//MetadataReader 的工厂类
MetadataReaderFactory readerfactory = new CachingMetadataReaderFactory(resourcePatternResolver);
for (Resource resource : resources) {
//用于读取类信息
MetadataReader reader = readerfactory.getMetadataReader(resource);
//扫描到的class
String classname = reader.getClassMetadata().getClassName();
Class<?> clazz = Class.forName(classname);
//将对应的类存在list
modelSet.add(clazz);
}
}
for (Class<?> aClass : modelSet) {
String tableComment = StringUtils.EMPTY;
//根据ApiModel获取表注释
ApiModel apiModel = aClass.getAnnotation(ApiModel.class);
if (apiModel != null) {
tableComment = apiModel.value();
}
//根据TableName获取表名称
TableName tableNameAnn = aClass.getAnnotation(TableName.class);
String tableName;
//有的实体类别没有通过TableName竹节布指定表明,就是通过类名转下划线的形式
if (tableNameAnn == null) {
String simpleName = aClass.getSimpleName();
//过滤掉不是数据库映射的类
if ("BaseEntity".equals(simpleName)) {
continue;
}
tableName = StringUtils.strip(humpToLine2(simpleName), "_");
} else {
tableName = tableNameAnn.value();
}
//校测表是否存在,返回表的所有字段
List<String> columnTable = baseModelMapper.columnTable(tableName);
//本类的所有字段
Field[] declaredFields = aClass.getDeclaredFields();
//父类所有字段
Field[] supercFields = aClass.getSuperclass().getDeclaredFields();
//数组合并
Field[] fields = new Field[declaredFields.length + supercFields.length];
System.arraycopy(declaredFields, 0, fields, 0, declaredFields.length);
System.arraycopy(supercFields, 0, fields, declaredFields.length, supercFields.length);
//字段集合
List<String> fieldList = new ArrayList<>();
//字段备注信息集合
Map<String, String> fieldRemarkMap = new HashMap<>(16);
//字段数据类型信息集合
Map<String, String> dataTypeMap = new HashMap<>(16);
for (Field declaredField : fields) {
//读取字段上ApiModelProperty注解的值
ApiModelProperty annotation = declaredField.getAnnotation(ApiModelProperty.class);
if (annotation == null) {
continue;
}
//读取字段上TableField注解的值
TableField tableField = declaredField.getAnnotation(TableField.class);
//去除无用字段
boolean serial = "serialVersionUID".equalsIgnoreCase(declaredField.getName());
//如果存在且exist值为false,说明该字段不与数据映射
boolean bool = tableField != null && !tableField.exist();
if (bool || serial) {
continue;
}
//字段名称
String fieldName = humpToLine2(declaredField.getName());
fieldList.add(fieldName);
//字段备注
String value = annotation.value();
fieldRemarkMap.put(fieldName, value);
//添加字段时最好在注解里备注好
String dataType = annotation.dataType();
if (StringUtils.isBlank(dataType)) {
dataType = declaredField.getType().getName();
}
dataTypeMap.put(fieldName, dataType);
}
//如果表不存在,添加表
if (CollectionUtils.isEmpty(columnTable)) {
//tableName 表明 ;;tableComment 表备注信息
baseModelMapper.createTable(tableName, tableComment);
}
for (String fieldName : fieldList) {
//与数据库字段比较是否存在,如果存才不操作,不存在就添加
boolean match = columnTable.stream().anyMatch(s -> s.equalsIgnoreCase(fieldName));
if (!match) {
//字段数据类型
String dataType = dataTypeMap.get(fieldName);
String fieldadd = propertyType(dataType, fieldName);
//备注信息
String comment = fieldRemarkMap.get(fieldName);
alterTable(tableName, fieldadd, comment);
}
}
}
} catch (Exception e) {
log.error(e.getMessage());
}
}
/**
* 更新字段
*
* @param tableName 表名称
* @param fieldadd 更新字段
* @param comment 字段备注
*/
public void alterTable(String tableName, String fieldadd, String comment) {
try {
//更新字段
baseModelMapper.alterTable(tableName, fieldadd, comment);
} catch (Exception e) {
log.error(e.getMessage());
}
}
/**
* @param type 数据类型
* @param name 表名称
* @return
*/
private static String propertyType(String type, String name) {
name = humpToLine2(name);
if ("java.util.Date".equals(type)) {
return name + StringUtils.SPACE + "datetime(0)";
} else if ("java.lang.String".equals(type)) {
return name + StringUtils.SPACE + "varchar(255)";
} else if ("java.lang.Integer".equals(type)) {
return name + StringUtils.SPACE + "int(11)";
} else if ("java.math.BigDecimal".equals(type)) {
return name + StringUtils.SPACE + "decimal(10,2)";
} else if ("java.lang.Long".equals(type)) {
return name + StringUtils.SPACE + "bigint(20)";
} else if ("java.lang.Boolean".equals(type)) {
return name + StringUtils.SPACE + "tinyint(2)";
} else if ("java.time.LocalDateTime".equals(type)) {
return name + StringUtils.SPACE + "datetime(0)";
}
return name + StringUtils.SPACE + type;
}
/**
* 31 * 驼峰转下划线
* 32
*/
public static String humpToLine2(String str) {
Matcher matcher = HUMP_PATTERN.matcher(str);
StringBuffer sb = new StringBuffer();
while (matcher.find()) {
matcher.appendReplacement(sb, "_" + matcher.group(0).toLowerCase());
}
matcher.appendTail(sb);
return sb.toString();
}
}