关于使用lambdaUpdate()导致updateAt不更新问题解决方案
很苦恼,为啥使用lambdaUpdate()会导致updateAt不更新呢,明明我也标明了注解。
明明我也有对应的config处理类进行自动填充但是为什么就是不更新呢???
这是因为:MyBatis-Plus 字段填充是必须依赖于实体对象,通过实体对象进行自动插入,如果参数中不包含实体对象,则无法触发字段自动填充
如下图所示 我们执行的方法:
然后通过一层层跟进去到源码处理层的时候我们会发现实体类为null是不处理的
固不会更新对应的updateAt也不会执行填充器了。
解决方案一
既然没有声明实体类不会进行自动填充那么我们声明不就好了吗~
这样就会进行自动填充啦
解决方案二
使用mybatis提供的插件进行解决,类似于mybatis-plus的乐观锁实现以及字段填充实现。
具体代码:
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.Constants;
import com.baomidou.mybatisplus.core.toolkit.support.ColumnCache;
import com.google.common.collect.Maps;
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.stereotype.Component;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.*;
import java.util.function.Supplier;
/**
* @className UpdatedAtFillPlugin
* @author Barcke
* @date 2021/4/28 下午7:30
* @version 1.0
* @slogan: 源于生活 高于生活
* @description:
**/
@Intercepts({@Signature(type= Executor.class, method="update", args={MappedStatement.class, Object.class})})
@Component
public class UpdatedAtFillPlugin implements Interceptor {
private static final String UPDATED_AT = "updatedAt";
private static final String UPDATE_AT = "update_at";
public static final Map<String, Supplier<Object>> SUPPORT_TIME_TYPE = Maps.newHashMap();
static {
SUPPORT_TIME_TYPE.put(LocalDateTime.class.getName(), LocalDateTime::now);
SUPPORT_TIME_TYPE.put(LocalDate.class.getName(), LocalDate::now);
SUPPORT_TIME_TYPE.put(LocalTime.class.getName(), LocalTime::now);
SUPPORT_TIME_TYPE.put(Date.class.getName(), Date::new);
}
@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();
}
// 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) && ReflectUtil.hasField(et.getClass(), UPDATED_AT)) {
this.setUpdatedAt(et);
return invocation.proceed();
}
//当没有定义实体类的时候 强行塞值~
if (paramMap.get("param2") instanceof LambdaUpdateWrapper) {
LambdaUpdateWrapper lambdaUpdateWrapper = (LambdaUpdateWrapper) paramMap.get("param2");
Field columnMap = ReflectUtil.getField(lambdaUpdateWrapper.getClass(), "columnMap");
columnMap.setAccessible(true);
Map<String, ColumnCache> map = (Map<String, ColumnCache>) columnMap.get(lambdaUpdateWrapper);
if (map.keySet().stream().anyMatch(k -> k.equalsIgnoreCase(UPDATED_AT))) {
lambdaUpdateWrapper.setSql(true, "updated_at='" + LocalDateTime.now() + "'");
return invocation.proceed();
}
//兼容 update_at问题~~~~
else if (map.values().stream().anyMatch(k -> k.getColumn().equalsIgnoreCase(UPDATE_AT))) {
lambdaUpdateWrapper.setSql(true, UPDATE_AT + "='" + LocalDateTime.now() + "'");
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;
}).forEach(field -> {
Supplier<Object> objectSupplier = SUPPORT_TIME_TYPE.get(field.getType().getName());
field.setAccessible(true);
entityReflect.set(field.getName(), objectSupplier.get());
});
}
return invocation.proceed();
}
private void setUpdatedAt(Object arg) throws IllegalAccessException, NoSuchFieldException {
Field declaredField = arg.getClass().getDeclaredField(UPDATED_AT);
Supplier<Object> objectSupplier = SUPPORT_TIME_TYPE.get(declaredField.getType().getName());
declaredField.setAccessible(true);
declaredField.set(arg, objectSupplier.get());
}
@Override
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
@Override
public void setProperties(Properties properties) {
}
}
这个时候我们就算没有给实体类此时也是会进入到mybatis到插件处理的,那么我们直接setUpdateAt字段即可。
当然这样有一定风险,如果字段名称不一致呢?如果字段类型不是datetime的呢?这些就需要我们根据业务去定制啦~
本文到此结束诺,希望对你有所帮助~