【Mybatis】Mybatis plus 生成数据更新字段置空的GeneratorWrapper

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的编写。

  • 8
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

生如夏花般绚丽

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值