• mybatis-plus调用update方法时,自动填充字段不生效(附解决方法)


    项目场景:

    定时任务,查询出数据后,将他发往mq队列,如果搭建集群相同的数据就会执行多次,所以使用乐观锁解决,同时需要更改更新时间一列,直接使用mybatisPlus的公共字段填充和乐观锁


    问题描述

    配置好mp的乐观锁和公共字段填充后,执行update语句,正常应该是

    UPDATE tb_task SET update_time=?,version=? WHERE (version = ? AND id = ?)

    结果变成了

    UPDATE tb_task SET WHERE (version = ? AND id = ?)

    因为除了这两个字段没有其他需要修改的字段所以直接就报错了,这么一看肯定是乐观锁和公共字段填充都失效了啊.

    @Scheduled(cron = "0 0/1 * * * ?")
        public void addCourseTask(){
            //查询1分钟之前的数据
            List<TbTask> list = tbTaskService.findBeforeMinuteList();
            for (TbTask tbTask : list) {
                //根据id version修改
                LambdaUpdateWrapper<TbTask> wrapper = new LambdaUpdateWrapper<>();
                wrapper.eq(TbTask::getId,tbTask.getId());
                        //.set(TbTask::getVersion,tbTask.getVersion()+1);
                //数据库中的乐观锁, 防止集群下的订单,重复向mq中发送数据
                if (tbTaskService.update(wrapper)) {
                    String mqExchange = tbTask.getMqExchange();
                    String mqRoutingkey = tbTask.getMqRoutingkey();
                    //向mq发送消息
                    rabbitTemplate.convertAndSend(mqExchange, mqRoutingkey, JSON.toJSONString(tbTask));
    
                }
    
    
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    原因分析:

    检查了几遍确定mp的乐观锁和公共字段填充都没有问题

    乐观锁

    1. 配置乐观锁插件(mp是3.5.1的)
    @Configuration
    @MapperScan("com.xly.mapper") //扫描mapper接口所在包
    public class MybatisPlusConfig {
    
        @Bean
        //配置分页插件
        public MybatisPlusInterceptor mybatisPlusInterceptor(){
            MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
            //分页
            interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
            //乐观锁
            interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
            return interceptor;
    
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    2.字段上打注解

    @TableField(value = "version")
        @Version
        private Integer version;
    
    • 1
    • 2
    • 3

    公共字段填充

    1. 实现MetaObjectHandler的公共填充类
    @Component
    public class MyMetaObjectHandler implements MetaObjectHandler {
        //mp执行添加操作,这个方法执行
        @Override
        public void insertFill(MetaObject metaObject) {
            metaObject.setValue("createTime",new Date());
            metaObject.setValue("updateTime",new Date());
        }
    
        //mp执行修改操作,这个方法执行
        @Override
        public void updateFill(MetaObject metaObject) {
            metaObject.setValue("updateTime",new Date());
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    1. 在字段上添加fill属性
    @TableField(value = "create_time",fill = FieldFill.INSERT)
        private Date createTime;
        @TableField(value = "update_time",fill = FieldFill.INSERT_UPDATE)
        private Date updateTime;
    
    • 1
    • 2
    • 3
    • 4

    经过查找资料后发现使用boolean update(Wrapper updateWrapper)这个方法,自动填充会失效
    大概原理就是boolean update(Wrapper updateWrapper)的底层实现为:

    default boolean update(Wrapper<T> updateWrapper) {
            return this.update((Object)null, updateWrapper);
        }
    
    • 1
    • 2
    • 3

    而属性自动填充需要从第一个参数获取Object实体类,自动填充的核心方法:populateKeys中会判断

    if (null == tableInfo) {
                /* 不处理 */
                return parameterObject;
            }
    
    • 1
    • 2
    • 3
    • 4

    tableInfo就是获取的实体类对象,所以导致属性自动填充失效.

    参考文章:如何解决mp调用update导致公共字段填充失效


    解决方案:

    我使用的是上面文章建议的方案一,也是最简单的方式:
    使用update的重载方法boolean update(T entity, Wrapper updateWrapper)

    修改后的代码如下:

    @Scheduled(cron = "0 0/1 * * * ?")
        public void addCourseTask(){
            //查询1分钟之前的数据
            List<TbTask> list = tbTaskService.findBeforeMinuteList();
            for (TbTask tbTask : list) {
                //根据id version修改
                LambdaUpdateWrapper<TbTask> wrapper = new LambdaUpdateWrapper<>();
                wrapper.eq(TbTask::getId,tbTask.getId());
                        //.set(TbTask::getVersion,tbTask.getVersion()+1);
                //数据库中的乐观锁, 防止集群下的订单,重复向mq中发送数据
                if (tbTaskService.update(tbTask,wrapper)) {
                    String mqExchange = tbTask.getMqExchange();
                    String mqRoutingkey = tbTask.getMqRoutingkey();
                    //向mq发送消息
                    rabbitTemplate.convertAndSend(mqExchange, mqRoutingkey, JSON.toJSONString(tbTask));
    
                }
    
    
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    执行sql语句:

    UPDATE tb_task
    SET create_time=?, update_time=?, delete_time=?, task_type=?, mq_exchange=?, mq_routingkey=?, request_body=?, status=?, version=? WHERE (version = ? AND id = ?)

  • 相关阅读:
    JVM基础学习(二):内存分配策略与垃圾收集技术
    -bash: ./deploy.sh: /bin/bash^M: bad interpreter: No such file or directory
    Qt 信号和槽执行线程问题
    C++:string的使用
    影视广告创意与制作(二)
    Embedding技术与应用(4): Embedding应用工程探析
    【C】关于柔性数组.简要的谈谈柔性数组
    python+selenium自动化测试项目实战
    js的slice()和splice()
    加速可编程创新,2023年英特尔FPGA中国技术日披露全矩阵FPGA产品与应用方案
  • 原文地址:https://blog.csdn.net/qq_45925787/article/details/126917402