• 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逻辑删除不能记录修改时间

  • 相关阅读:
    Java线程基础(二)
    Java线程基础(一)
    泛型集合List的详细用法
    Java中日期格式(String、Date、Calendar)的相互转换
    重写Java中包装类的方法
    Java的集合框架(第一次小结)
    node.js 调用mysql 数据库
    win10 系统解决mysql中文乱码问题
    vue-echarts图表
    文件上传的几个例子
  • 原文地址:https://www.cnblogs.com/hongdada/p/16826353.html
Copyright © 2020-2023  润新知