MyBatis-Plus实现悲观锁与乐观锁项目实践指南
时间:2026-08-18 | 作者:星际追番人 | 阅读:0MyBatis-Plus 对乐观锁的支持很完善,内置了插件,使用起来非常方便。
但悲观锁没有专门封装,通常需要依赖底层 SQL 或原生 MyBatis 来实现。下面分别说明这两种锁的项目实践方法。
一、乐观锁(Optimistic Lock)
乐观锁的核心思路很简单:更新时先校验版本号,只有版本号一致才允许更新,同时将版本号 +1。
MyBatis-Plus 已经为此提供了专门插件,可以减少很多重复代码。
1. 实体类加@Version注解
@Data
public class Product {
private Long id;
private String name;
private Integer price;
@Version // 乐观锁版本号字段
private Integer version;
}
2. 配置乐观锁拦截器
@Configuration
public class MybatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
// 添加乐观锁拦截器
interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
return interceptor;
}
}
3. 使用方式
// 1. 先查询出实体(version 会被查出来) Product product = productService.getById(1L); // 2. 修改数据 product.setPrice(product.getPrice() + 100); // 3. 执行更新(MP 会自动在 WHERE 条件中加上 version = ) // 生成的 SQL 类似: // UPDATE product SET price=, version=version+1 WHERE id= AND version= boolean success = productService.updateById(product);
4. 注意事项
| 注意点 | 说明 |
|---|---|
| version 字段类型 | 支持 int、Integer、long、Long、Date、Timestamp |
| 必须查后更新 | updateById(entity) 才会生效,直接 new 一个对象更新不会触发乐观锁 |
| 自增逻辑 | 框架自动 version + 1,无需手动设置 |
| 更新失败 | 如果返回 false,说明数据已被他人修改,需要业务上重试或抛异常 |
5. 批量更新也支持
批量更新时,每个实体的 version 都会进入 WHERE 条件。
// 批量更新时,每个实体的 version 都会参与 WHERE 条件 productService.updateBatchById(productList);
二、悲观锁(Pessimistic Lock)
悲观锁和乐观锁不同。MyBatis-Plus 没有提供专门的悲观锁插件。
这是因为悲观锁本质上属于数据库层机制,通常依赖 SQL 的 FOR UPDATE 实现。所以在这种场景下,往往需要自己编写 SQL。
方式一:手写 SQL(推荐)
在 Mapper 中直接通过 @Select 编写带 FOR UPDATE 的 SQL,方式最清晰,也最容易控制。
public interface ProductMapper extends BaseMapper{ @Select("SELECT * FROM product WHERE id = #{id} FOR UPDATE") Product selectByIdForUpdate(Long id); }
Service 层调用时,一定要加 @Transactional。因为 FOR UPDATE 必须依赖事务才能生效。
@Service public class ProductServiceImpl extends ServiceImplimplements ProductService { @Transactional // 必须在事务中,FOR UPDATE 才有效 public void deductStock(Long id) { // 1. 加锁查询 Product product = baseMapper.selectByIdForUpdate(id); // 2. 执行业务逻辑 if (product.getStock() > 0) { product.setStock(product.getStock() - 1); updateById(product); } } }
方式二:用 Wrapper 拼接(不推荐,容易出问题)
这种方式不推荐。
FOR UPDATE需要写在 SQL 末尾,QueryWrapper 不容易精确控制位置。- MP 的 selectOne 等方法,也不支持直接附加
FOR UPDATE。
方式三:XML 方式
如果项目本身使用 XML 管理 SQL,也可以通过 XML 编写悲观锁查询。
三、对比与选型
| 特性 | 乐观锁 | 悲观锁 |
|---|---|---|
| MP 支持 | 原生插件支持 | 需手写 SQL |
| 实现机制 | 版本号控制 | SELECT ... FOR UPDATE |
| 性能 | 高(无锁等待) | 低(有锁竞争、阻塞) |
| 适用场景 | 读多写少、冲突概率低 | 写多读少、冲突概率高、强一致性 |
| 失败处理 | 更新失败需重试 | 排队等待,不会失败 |
| 事务要求 | 非必须 | 必须在事务中 |
四、最佳实践建议
优先用乐观锁。大多数互联网场景都是读多写少,乐观锁性能更好,而且 MyBatis-Plus 已经提供了完善支持,接入成本很低。
悲观锁用于强一致性场景。例如库存扣减、金融转账这类高并发写操作,冲突概率高。此时即使线程需要排队,也要优先保证数据正确,悲观锁会更稳妥。
乐观锁失败后要考虑重试机制。如果更新失败,可以重新查询最新数据后再次提交。
// 简单的重试机制
public boolean updateWithRetry(Product product, int maxRetries) {
for (int i = 0; i < maxRetries; i++) {
if (productService.updateById(product)) {
return true;
}
// 重新查询最新数据
product = productService.getById(product.getId());
}
throw new RuntimeException("更新失败,请重试");
}
免责声明:文中图文均来自网络,如有侵权请联系删除,心愿游戏发布此文仅为传递信息,不代表心愿游戏认同其观点或证实其描述。
相关文章
更多-
- Qwen3.7-Plus发布:11小时自主闭环开发真实APP与GUI编程
- 时间:2026-08-21
-
- Google AI Plus月费降至4.99美元 存储空间翻倍至400GB
- 时间:2026-08-21
-
- Google AI Plus服务降价升级:存储空间同步扩容
- 时间:2026-08-18
-
- MyBatisPlus自定义TypeHandler实现方法与示例
- 时间:2026-08-18
-
- ChatGPT Plus 8美元重置功能上线,额度用完无需再等
- 时间:2026-08-17
-
- 阿里开源Qwen3.8-27B模型:270亿参数编程能力超越Qwen3.7-Plus
- 时间:2026-08-16
-
- ChatGPT Plus如何实现Codex登录权限与使用教程
- 时间:2026-08-12
-
- BTF 3.0无线主机发售 270K Plus+5070 Ti售价17499元
- 时间:2026-08-10
精选合集
更多大家都在玩
大家都在看
更多-
- 糖尿病完全不能吃糖吗
- 时间:2026-09-15
-
- 蚂蚁庄园小课堂2026年9月16日最新题目答案
- 时间:2026-09-15
-
- 小鸡答题今天的答案是什么2026年9月16日
- 时间:2026-09-15
-
- 蚂蚁庄园每日答题答案2026年9月16日
- 时间:2026-09-15
-
- 以下哪种粮食是酿造绍兴黄酒的主要原料 蚂蚁庄园今日答案9月16日
- 时间:2026-09-15
-
- 劝学名句“及时当勉励,岁月不待人”出自哪位诗人 蚂蚁庄园今日答案9.16
- 时间:2026-09-15
-
- 蚂蚁庄园今天答题答案2026年9月16日
- 时间:2026-09-15
-
- 蚂蚁庄园答题今日答案2026年9月16日
- 时间:2026-09-15
