项目场景
做定时任务,查询出数据后,将他发往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)); } } }
原因分析
检查了几遍确定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; } }
2.字段上打注解
@tablefield(value = "version") @version private integer version;
公共字段填充
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()); } }
2.在字段上添加fill属性
@tablefield(value = "create_time",fill = fieldfill.insert) private date createtime; @tablefield(value = "update_time",fill = fieldfill.insert_update) private date updatetime;
经过查找资料后发现使用boolean update(wrapper updatewrapper)这个方法,自动填充会失效
大概原理就是boolean update(wrapper updatewrapper)的底层实现为:
default boolean update(wrapper<t> updatewrapper) { return this.update((object)null, updatewrapper); }
而属性自动填充需要从第一个参数获取object实体类,自动填充的核心方法:populatekeys中会判断
if (null == tableinfo) { /* 不处理 */ return parameterobject; }
tableinfo就是获取的实体类对象,所以导致属性自动填充失效。
解决方案
我使用的是上面文章建议的方案一,也是最简单的方式:
使用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)); } } }
执行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 = ?)
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持代码网。
发表评论