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
关于删除操作,比如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⾃动填充失效和条件为空报错的问题