mybatis plus 数据更新置空需求
在使用MybatisPlus的时候,默认的Update
系列方法,都是为null
不进行更新的,但是在某些场景是需要进行数据重置和置空的
官方提供的方法是在字段上添加@TableField(updateStrategy = FieldStrategy.ALWAYS)
注解
@TableField(updateStrategy = FieldStrategy.ALWAYS)
private BigDecimal field;
但是在常规开发中,对应的entity
都是公用的,没必要为一个场景添加一个新的实体类 增加维护成本,也不想自己一个字段一个字段的写updateWrapper.set
于是写了一个反射组装updateWrapper.set
的方法
@Component
public class CrmUpdateGenerator {
@Resource
SqlSessionFactory sqlSessionFactory;
/**
* 创建UpdateWrapperGenerator
*
* @param entity 实体
* @param entityClass 实体类
* @param mapperType
* @param ignoreProperties 属性
* @return
*/
public <T> UpdateWrapper createUpdateWrapper(T entity, Class<T> entityClass, Class<? extends BaseMapper<T>> mapperType, String... ignoreProperties) {
UpdateWrapper updateWrapper = new UpdateWrapper<>();
List<Field> fields = getAllFields(entityClass);
// 对应Mapper缓存添加是懒加载 若首次调用 不调用此方法 无法获取到COLUMN_CACHE_MAP映射
sqlSessionFactory.getConfiguration().addMapper(mapperType);
// 获取字段映射
Map<String, ColumnCache> columnMap = LambdaUtils.getColumnMap(entityClass);
for (Field field : fields) {
try {
field.setAccessible(true);
Object value = field.get(entity);
boolean isIgnore = false;
for (String ignoreProperty : ignoreProperties) {
if (ignoreProperty.equals(field.getName())) {
isIgnore = true;
break;
}
}
if (isIgnore) {
continue;
}
TableField annotation = field.getAnnotation(TableField.class);
if (Objects.nonNull(annotation)){
if (!annotation.exist()){
continue;
}
}
// 拼装字段
ColumnCache columnCache = columnMap.get(LambdaUtils.formatKey(field.getName()));
if (Objects.nonNull(columnCache)){
updateWrapper.set(columnCache.getColumn(), value);
}
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
return updateWrapper;
}
public List<Field> getAllFields(Class<?> clazz) {
List<Field> fields = new ArrayList<>();
while (clazz != null) {
for (Field field : clazz.getDeclaredFields()) {
fields.add(field);
}
clazz = clazz.getSuperclass();
}
return fields;
}
}
解析:
反射获取字段,无视掉ignoreProperties的和TableField(exist=false)
的
获取到字段后要根据fieldName获取字段映射从COLUMN_CACHE_MAP
中获取
/**
* 获取实体对应字段 MAP
*
* @param clazz 实体类
* @return 缓存 map
*/
public static Map<String, ColumnCache> getColumnMap(Class<?> clazz) {
return CollectionUtils.computeIfAbsent(COLUMN_CACHE_MAP, clazz.getName(), key -> {
TableInfo info = TableInfoHelper.getTableInfo(clazz);
return info == null ? null : createColumnCacheMap(info);
});
}
由上可以看出缓存是缓存的是clazz.getName(), TableInfoHelper.getTableInfo(clazz)
看下TableInfo
/**
* <p>
* 实体类反射获取表信息【初始化】
* </p>
*
* @param clazz 反射实体类
* @return 数据库表反射信息
*/
private static synchronized TableInfo initTableInfo(Configuration configuration, String currentNamespace, Class<?> clazz) {
/* 没有获取到缓存信息,则初始化 */
/* 初始化表名相关 */
/* 初始化字段相关 */
/* 自动构建 resultMap */
TABLE_INFO_CACHE.put(clazz, tableInfo);
TABLE_NAME_INFO_CACHE.put(tableInfo.getTableName(), tableInfo);
/* 缓存 lambda */
LambdaUtils.installCache(tableInfo);
return tableInfo;
}
可以看出在initTableInfo
的时候会进行缓存的初始化,而什么时候调用呢,根据调用栈来看:
// 使用自己的 MybatisMapperRegistry
MybatisConfiguration.addMapper
// 注入
MybatisMapperRegistry.addMapper
// 检查SQL是否注入 并添加缓存
AbstractSqlInjector
@Override
public void inspectInject(MapperBuilderAssistant builderAssistant, Class<?> mapperClass) {
Class<?> modelClass = ReflectionKit.getSuperClassGenericType(mapperClass, Mapper.class, 0);
if (modelClass != null) {
String className = mapperClass.toString();
Set<String> mapperRegistryCache = GlobalConfigUtils.getMapperRegistryCache(builderAssistant.getConfiguration());
if (!mapperRegistryCache.contains(className)) {
// 此处初始化的时候会将表结构和entity映射注入缓存
TableInfo tableInfo = TableInfoHelper.initTableInfo(builderAssistant, modelClass);
List<AbstractMethod> methodList = this.getMethodList(mapperClass, tableInfo);
if (CollectionUtils.isNotEmpty(methodList)) {
// 循环注入自定义方法
methodList.forEach(m -> m.inject(builderAssistant, mapperClass, modelClass, tableInfo));
} else {
logger.debug(mapperClass.toString() + ", No effective injection method was found.");
}
mapperRegistryCache.add(className);
}
}
}
接下来看怎么获取到对应的MybatisConfiguration
,Mybatis工作流程如下:
由于它继承了Configuration
,然后可以根据Mybatis
的工作流程可以看出,我们可以在SqlSessionFactory
中获取到进行操作
// 对应Mapper缓存添加是懒加载 若首次调用 不调用此方法 无法获取到COLUMN_CACHE_MAP映射
sqlSessionFactory.getConfiguration().addMapper(mapperType);
至此,完成Generator
的编写。