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

本文介绍了在使用MyBatis-Plus的lambdaUpdate()方法时,updateAt字段未更新的问题及解决方案。问题在于实体对象未被正确传递,导致字段填充失效。解决方案包括在更新时明确指定实体对象,或者使用MyBatis插件进行字段自动填充。详细解析了源码并提供了自定义拦截器的示例代码。
摘要由CSDN通过智能技术生成

关于使用lambdaUpdate()导致updateAt不更新问题解决方案

很苦恼,为啥使用lambdaUpdate()会导致updateAt不更新呢,明明我也标明了注解。
image

明明我也有对应的config处理类进行自动填充但是为什么就是不更新呢???
image

这是因为:MyBatis-Plus 字段填充是必须依赖于实体对象,通过实体对象进行自动插入,如果参数中不包含实体对象,则无法触发字段自动填充

如下图所示 我们执行的方法:
image

然后通过一层层跟进去到源码处理层的时候我们会发现实体类为null是不处理的
image

固不会更新对应的updateAt也不会执行填充器了。

解决方案一

既然没有声明实体类不会进行自动填充那么我们声明不就好了吗~
image

这样就会进行自动填充啦

解决方案二

使用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的呢?这些就需要我们根据业务去定制啦~

本文到此结束诺,希望对你有所帮助~

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值