当前位置: 代码网 > it编程>编程语言>Java > MybatisPlus实现悲观锁 & 乐观锁的项目实践

MybatisPlus实现悲观锁 & 乐观锁的项目实践

2026年08月05日 Java 我要评论
mybatis-plus 对乐观锁提供了原生支持,但对悲观锁没有专门封装,需要借助底层 sql 或 mybatis 原生方式实现。一、乐观锁(optimistic lock)mybatis-plus

mybatis-plus 对乐观锁提供了原生支持,但对悲观锁没有专门封装,需要借助底层 sql 或 mybatis 原生方式实现。

一、乐观锁(optimistic lock)

mybatis-plus 内置了乐观锁插件,核心思路是:更新时检查版本号,版本号匹配才更新,同时版本号+1

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 条件
productservice.updatebatchbyid(productlist);

二、悲观锁(pessimistic lock)

mybatis-plus 没有提供专门的悲观锁插件因为悲观锁是数据库层面的机制,需要通过 sql 的 for update 实现。

方式一:手写 sql(推荐)

在 mapper 中用 @select 手写带 for update 的 sql:

public interface productmapper extends basemapper<product> {
    
    @select("select * from product where id = #{id} for update")
    product selectbyidforupdate(long id);
}

service 层调用:

@service
public class productserviceimpl extends serviceimpl<productmapper, product> 
    implements 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 方式

<!-- productmapper.xml -->
<select id="selectbyidforupdate" resulttype="com.example.entity.product">
    select * from product where id = #{id} for update
</select>

三、对比与选型

特性乐观锁悲观锁
mp 支持✅ 原生插件支持❌ 需手写 sql
实现机制版本号控制select ... for update
性能高(无锁等待)低(有锁竞争、阻塞)
适用场景读多写少、冲突概率低写多读少、冲突概率高、强一致性
失败处理更新失败需重试排队等待,不会失败
事务要求非必须必须在事务中

四、最佳实践建议

优先用乐观锁:绝大多数互联网场景读多写少,乐观锁性能更好,mp 支持也完善。

悲观锁用于强一致性场景:如库存扣减、金融转账等高并发写场景。

乐观锁失败重试

// 简单的重试机制
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("更新失败,请重试");
}

到此这篇关于mybatisplus实现悲观锁 &amp; 乐观锁的项目实践的文章就介绍到这了,更多相关mybatisplus 悲观锁和乐观锁内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2026  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com