mybatis-plus - 乐观锁的使用
mybatis-plus - 乐观锁的使用
- 添加版本号更新插件
@BeanpublicMybatisPlusInterceptormybatisPlusInterceptor(){MybatisPlusInterceptorinterceptor=newMybatisPlusInterceptor();interceptor.addInnerInterceptor(newOptimisticLockerInnerInterceptor());returninterceptor;}实体的乐观锁字段 version添加
@Version注解注意:数据库也需要添加 version 字段
ALTERTABLEUSERADDVERSIONINTDEFAULT1;#int类型 乐观锁字段- 支持的数据类型只有: int, Integer, long, Long, Date, Timestamp, LocalDateTime
- 仅支持
updateById(entity)和update(entity, wrapper),前提entity != null。
@VersionprivateIntegerversion;- 正常更新使用即可
//演示乐观锁生效场景@TestpublicvoidtestQuick7(){// 先查询, 在更新, 获取version数据// 同时查询两条, 但是 version 唯一, 最后更新的失败Useruser=userMapper.selectById(5);Useruser1=userMapper.selectById(5);user.setAge(20);user1.setAge(30);userMapper.updateById(user);// 乐观锁生效,失败!userMapper.updateById(user1);}有个坑
坑1 - 新增的时候 version 没值,第一次更新直接失败
如果新增没带、数据库没设置 default 1,insert 之后 version=null。
第一次 updateById 时,where 条件version=null,数据库判断null = null不是 true,更新行数 0,直接报冲突。
✅ 规范:数据库字段version int default 1,实体不用手动 set version,新增 MP 不会处理 version,靠数据库默认值。
坑2 - 只判断 rows == 0,但是分不清 “数据不存在” 和 “版本冲突”
introws=mapper.updateById(entity);if(rows==0){thrownewRuntimeException("数据被别人修改,请重试");}问题:rows=0有两种可能:
- id 不存在;
- id 存在,但是 version 不匹配(并发修改)。
上面代码会把这两种情况混在一起抛同一个异常,前端提示会误导用户。
✅ 业务优化:更新前必须先selectById,判断记录是否存在,再执行更新;rows=0才判定为乐观锁冲突。
Useruser=mapper.selectById(id);if(user==null){thrownewBusinessException("记录不存在");}// 修改字段introws=mapper.updateById(user);if(rows==0){thrownewBusinessException("数据已被其他用户修改,请刷新页面重试");}updateById(entity)✅ 最稳,业务首选。
最常用,不容易踩坑。先查,拿到version,改字段,updateById。update(entity, wrapper)✅ 能用,但尽量少用。
为什么少用:where条件 = 主键(如果实体带id) + wrapper条件 +AND version=?,条件叠加容易写出意料之外的SQL。例子:实体id=1,wrapper写
eq(id,2),where就变成id=1 AND id=2 AND version=?,直接更新0行,这种bug很难排查。
很多新人踩这个坑:实体带id,wrapper又写id条件,冲突。