MyBatis-Plus 乐观锁需三者齐备:数据库 version 字段(INT NOT NULL DEFAULT 0)、实体类 @Version 标注(Integer/Long 类型)、显式注册 OptimisticLockerInnerInterceptor 拦截器;仅 updateById 或带主键条件的 update 支持,失败抛 OptimisticLockException。

MyBatis-Plus 的 @Version 注解本身不触发乐观锁——它只是标记字段,真正起作用的是拦截器在 SQL 层注入的版本校验逻辑。必须三者齐备:数据库有 version 字段、实体类用 @Version 标记、Spring 容器中注册了乐观锁拦截器,缺一不可。
数据库和实体类准备
确保表中存在数值型 version 字段,推荐定义为 INT NOT NULL DEFAULT 0(或 1),避免 NULL 导致 WHERE 条件恒假:
- MySQL 示例:
ALTER TABLE user ADD COLUMN version INT NOT NULL DEFAULT 0; - 实体类字段类型需为
Integer、Long或AtomicInteger,不能是String或LocalDateTime - 注解写法:
@Version private Integer version;,且该字段应参与 update 操作(非 transient)
拦截器必须显式注册(关键!)
MyBatis-Plus 不会自动启用乐观锁,必须手动配置 MybatisPlusInterceptor 并添加 OptimisticLockerInnerInterceptor:
- Spring Boot 3.5+ 正确写法(老版
OptimisticLockerPlugin已废弃):
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
return interceptor;
}
- 若同时使用分页插件,乐观锁拦截器必须排在
PaginationInnerInterceptor之前 - 多数据源场景下,每个
SqlSessionFactory都要单独配置该拦截器
更新操作必须符合触发条件
乐观锁只在特定方法调用时生效,不是所有 update 写法都支持:
立即学习“Java免费学习笔记(深入)”;
- ✅ 有效:调用
mapper.updateById(entity),且 entity 中version值非 null(如从 DB 查询后修改再提交) - ✅ 有效:使用
update(entity, wrapper),其中 wrapper 包含主键条件 - ❌ 无效:用
lambdaUpdate().eq(...).set(...)直接拼 WHERE,未携带 version 字段,不会注入版本检查 - ⚠️ 注意:
updateById()生成的 SQL 是UPDATE ... SET ... WHERE id = ? AND version = ?,成功后自动将 version + 1
失败处理与调试要点
并发冲突时不会静默失败,而是抛出明确异常:
- 异常类型为
OptimisticLockException,不是通用SQLException - 捕获后可选择重试(重新查最新数据 → 修改 → 再提交)、提示用户“数据已被他人修改”,或记录日志告警
- 常见失效原因排查:字段类型不对、数据库列允许 NULL、拦截器未注册、用了不支持的 update 方式、version 值为 null 传入


















