MybatisPlus 部分场景自动填充失败

Update场景:

  leadsInfoService.lambdaUpdate()
                    .in(LeadsInfoEntity::getId, entityList.stream().map(LeadsInfoEntity::getId).collect(Collectors.toList()))
                    .set(LeadsInfoEntity::getIsDeleted, IsDeletedEnum.Y.getCode())
                    .update();

上面这种使用mp的批量修改方法,那么自动填充修改时间,修改人名称将无效

    @Override
    public void updateFill(MetaObject metaObject) {
        setFieldValByName("modifier", authManager.getLoginName(), metaObject);
        setFieldValByName("modifyUserId", authManager.getUserId(), metaObject);
        setFieldValByName("gmtModified", LocalDateTime.now(), metaObject);
    }

使用LambdaWapper方法更新部分字段,并不传递实体对象,无法触发自动填充,则自动填充失败

上面的可以修改为:

entityList.forEach(x -> {
				leadsInfoService.lambdaUpdate()
						.eq(LeadsInfoEntity::getId, x.getId())
						.set(LeadsInfoEntity::getIsDeleted, IsDeletedEnum.Y.getCode())
						.update(new LeadsInfoEntity());
			});

使用上面这种方式可以自动填充,因为传递了对象

⽽其他全量更新⽅法: updateById(T entity),updateById(T entity),updateBatchById(Collection entityList),saveOrUpdate(T entity) 都包含了实体,不受影响

关于删除操作,比如remove方法,实践下来,暂时没有方法来填充更新其他字段.

使用拦截器自动填充更新字段

import cn.hutool.core.util.ReflectUtil;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.Constants;
import com.baomidou.mybatisplus.core.toolkit.support.ColumnCache;
import com.zhongan.leads.config.AuthManager;
import lombok.Data;
import lombok.experimental.Accessors;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.plugin.*;
import org.joor.Reflect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.time.LocalDateTime;
import java.util.*;

/**
 * @author qhong
 * @date 2022/5/21 15:17
 **/
@Intercepts({@Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class})})
@Component
public class MybatisUpdateUserSqlInterceptor implements Interceptor {

	private static final String param2 = "param2";
	private static final String columnMap = "columnMap";


	@Autowired
	private AuthManager authManager;


	@Override
	public Object intercept(Invocation invocation)
			throws InvocationTargetException, IllegalAccessException, NoSuchFieldException {

		Object[] args = invocation.getArgs();
		MappedStatement ms = (MappedStatement) args[0];
		if (SqlCommandType.UPDATE != ms.getSqlCommandType()) {
			return invocation.proceed();
		}

		Object arg = args[1];
		if (arg == null) {
			return invocation.proceed();
		}
		List<BaseUpdateInfo> list = getBaseUpdateInfoList();
		if (CollectionUtils.isEmpty(list)) {
			return invocation.proceed();
		}
		// arg1为空时,无参Mapper执行
		// arg1不为空时
		//  如果是Map,则是有参数Mapper
		//  如果是有TableName注解,则是lambdaUpdate
		Class<?> entityClazz = arg.getClass();
		if (arg instanceof Map) {
			Map paramMap = (Map) arg;
			Object et = paramMap.getOrDefault(Constants.ENTITY, null);
			if (Objects.nonNull(et)) {
				//更新对象 update(new Entity())
				for (BaseUpdateInfo x : list) {
					if (ReflectUtil.hasField(et.getClass(), x.getEntityName())) {
						Field declaredField = et.getClass().getDeclaredField(x.getEntityName());
						declaredField.setAccessible(true);
						if (declaredField.get(et) == null) {
							declaredField.set(et, x.getValue());
						}
					}
				}
				return invocation.proceed();
			}

			for (Object o : paramMap.keySet()) {
				//当没有定义实体类的时候 强行塞值,比如lambdaUpdate..update()  .remove()
				if (o.toString().equalsIgnoreCase(param2) && paramMap.get(o) != null && paramMap
						.get(o) instanceof LambdaUpdateWrapper) {
					LambdaUpdateWrapper lambdaUpdateWrapper = (LambdaUpdateWrapper) paramMap.get(o);
					Field columnMapField = ReflectUtil.getField(lambdaUpdateWrapper.getClass(), columnMap);
					columnMapField.setAccessible(true);
					Map<String, ColumnCache> map = (Map<String, ColumnCache>) columnMapField.get(lambdaUpdateWrapper);
					if (map != null) {
						//更新,map有值,当使用remove操作时,param1,setsql也无效
						map.keySet().stream().forEach(k -> {
							list.forEach(x -> {
								if (k.equalsIgnoreCase(x.getEntityName()) && !lambdaUpdateWrapper.getSqlSet()
										.toLowerCase()
										.contains(x.getFieldName())) {
									lambdaUpdateWrapper.setSql((true), x.getFieldName() + "='" + x.getValue() + "'");
								}
							});
						});
					}
				}
			}

			return invocation.proceed();
		} else if (entityClazz.isAnnotationPresent(TableName.class)) {
			// 当使用lambdaUpdate时,mybatisPlus会将其解析为select标签,不会走到MetaObjectHandlerConfig自动填充更新时间
			Reflect entityReflect = Reflect.on(arg);
			Arrays.stream(entityClazz.getDeclaredFields()).filter(e -> {
				TableField tableField = e.getAnnotation(TableField.class);
				return tableField != null && (tableField.fill() == FieldFill.INSERT_UPDATE
						|| tableField.fill() == FieldFill.UPDATE);
			}).forEach(field -> {
				list.forEach(x -> {
					if (field.getName().equalsIgnoreCase(x.getEntityName())) {
						field.setAccessible(true);
						entityReflect.set(field.getName(), x.getValue());
					}
				});
			});
		}
		return invocation.proceed();
	}


	@Override
	public Object plugin(Object target) {
		return Plugin.wrap(target, this);
	}

	@Override
	public void setProperties(Properties properties) {

	}
	

	/**
	 * 获取修改的属性字段列表
	 */
	private List<BaseUpdateInfo> getBaseUpdateInfoList() {
		return
				Arrays.asList(
						BaseUpdateInfo.of().setFieldName("modifier").setEntityName("modifier")
								.setValue(authManager.getLoginName()),
						BaseUpdateInfo.of().setFieldName("modify_user_id").setEntityName("modifyUserId")
								.setValue(authManager.getUserId()),
						BaseUpdateInfo.of().setFieldName("gmt_modified").setEntityName("gmtModified")
								.setValue(LocalDateTime.now()));
	}


	@Data(staticConstructor = "of")
	@Accessors(chain = true)
	static class BaseUpdateInfo {

		/**
		 * 表字段名称
		 */
		private String fieldName;

		/**
		 * 对象属性名称
		 */
		private String entityName;

		/**
		 * 对应值
		 */
		private Object value;
	}
}

remove特例

不管是使用lambdaUpdate中的remove

还是使用removeById

都不会填充字段,就是不会更新最后修改人,最后修改时间

优化版本拦截器

import com.baomidou.mybatisplus.core.toolkit.PluginUtils;
import com.baomidou.mybatisplus.extension.handlers.AbstractSqlParserHandler;
import com.zhongan.leads.config.AuthManager;
import com.zhongan.leads.utils.DateFormatUtils;
import java.sql.Connection;
import java.util.Map.Entry;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.mapping.StatementType;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.*;

/**
 * @author qhong
 * @date 2022/5/21 15:17
 **/

@Intercepts({@Signature(
		type = StatementHandler.class,
		method = "prepare",
		args = {Connection.class, Integer.class}
)})
@Component
public class MybatisUpdateUserSqlInterceptor extends AbstractSqlParserHandler implements Interceptor {


	@Autowired
	private AuthManager authManager;

	@Override
	public Object intercept(Invocation invocation) throws Throwable {
		StatementHandler statementHandler = PluginUtils.realTarget(invocation.getTarget());
		MetaObject metaObject = SystemMetaObject.forObject(statementHandler);
		this.sqlParser(metaObject);
		MappedStatement mappedStatement = (MappedStatement) metaObject.getValue("delegate.mappedStatement");
		if (SqlCommandType.UPDATE == mappedStatement.getSqlCommandType() && StatementType.CALLABLE != mappedStatement
				.getStatementType()) {

			BoundSql boundSql = (BoundSql) metaObject.getValue("delegate.boundSql");
			//获取已经构造好的SQL
			String sql = boundSql.getSql();
			//获取映射的参数
			List<ParameterMapping> mappings = new ArrayList(boundSql.getParameterMappings());
			//假如参数中不包含要构造的参数,手动写入
			Map<String, Object> fieldsMap = getBaseUpdateMap();
			for (Entry<String, Object> map : fieldsMap.entrySet()) {
				if (sql.indexOf(map.getKey()) < 0) {
					//写入更新时间
					sql = sql.replace("SET", "SET " + map.getKey() + " = '" + map.getValue() + "',");
				}
			}
			metaObject.setValue("delegate.boundSql.sql", sql);
			metaObject.setValue("delegate.boundSql.parameterMappings", mappings);
		}
		return invocation.proceed();
	}

	@Override
	public Object plugin(Object target) {
		return target instanceof StatementHandler ? Plugin.wrap(target, this) : target;
	}

	private Map<String, Object> getBaseUpdateMap() {
		Map<String, Object> params = new HashMap<>(4);
		params.put("modify_user_id", authManager.getUserId());
		params.put("modifier", authManager.getLoginName());
		params.put("gmt_modified", DateFormatUtils.format(new Date(), DateFormatUtils.DATE_TIME_PATTERN));
		return params;
	}
}

这种方式比较简便,并且兼容remove方法.

参考:

关于Mybatis-plusLambda⾃动填充失效和条件为空报错的问题

Mybatis-plus Lambda自动填充 MetaObjectHandler 失效 解决方案

mybaitis逻辑删除不能记录修改时间

posted @ 2022-10-25 21:11  hongdada  阅读(955)  评论(0编辑  收藏  举报