MyBatisPlus乐观锁和悲观锁的实现示例
作者:222you
1.场景
一件商品,成本价是80元,售价是100元。老板先是通知小李,说你去把商品价格增加50元。小 李正在玩游戏,耽搁了一个小时。正好一个小时后,老板觉得商品价格增加到150元,价格太高,可能会影响销量。又通知小王,你把商品价格降低30元。
此时,小李和小王同时操作商品后台系统。小李操作的时候,系统先取出商品价格100元;小王 也在操作,取出的商品价格也是100元。小李将价格加了50元,并将100+50=150元存入了数据 库;小王将商品减了30元,并将100-30=70元存入了数据库。是的,如果没有锁,小李的操作就 完全被小王的覆盖了。
现在商品价格是70元,比成本价低10元。几分钟后,这个商品很快出售了1千多件商品,老板亏1 万多。
2.乐观锁和悲观锁
上面的故事,如果是乐观锁,小王保存价格前,会检查下价格是否被人修改过了。如果被修改过 了,则重新取出的被修改后的价格, 150元,这样他会将120元存入数据库。
如果是悲观锁,小李取出数据后,小王只能等小李操作完之后,才能对价格进行操作,也会保证 最终的价格是120元。
3.乐观锁实现
添加插件:
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
// 添加分页插件
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
// 添加乐观锁插件
interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
return interceptor;
}创建表:
CREATE TABLE t_product
(
id BIGINT(20) NOT NULL COMMENT '主键ID',
NAME VARCHAR(30) NULL DEFAULT NULL COMMENT '商品名称 ',
price INT(11) DEFAULT 0 COMMENT '价格 ',
VERSION INT(11) DEFAULT 0 COMMENT '乐观锁版本号 ',
PRIMARY KEY (id)
);
添加数据:
INSERT INTO t_product (id, NAME, price) VALUES (1, '外星人笔记本 ', 100);
如图:

编写实体类:
package com.qcby.entity;
import com.baomidou.mybatisplus.annotation.Version;
import lombok.Data;
@Data
public class Product {
private Long id;
private String name;
private Integer price;
@Version
private Integer version;
}mapper:
package com.qcby.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.qcby.entity.Product;
import org.springframework.stereotype.Repository;
@Repository
public interface ProductMapper extends BaseMapper<Product> {
}
测试:
package com.qcby;
import com.qcby.entity.Product;
import com.qcby.mapper.ProductMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
public class MybatisTestOptimisticLock {
@Autowired
private ProductMapper productMapper;
@Test
public void testOptimisticLock() {
// 小李查询商品信息
Product productLi = productMapper.selectById(1L);
System.out.println("小李查询价格: " + productLi.getPrice() + ", 版本: " + productLi.getVersion());
// 小王查询商品信息
Product productWang = productMapper.selectById(1L);
System.out.println("小王查询价格: " + productWang.getPrice() + ", 版本: " + productWang.getVersion());
// 小李修改价格 +50
productLi.setPrice(productLi.getPrice() + 50);
int resultLi = productMapper.updateById(productLi);
System.out.println("小李修改结果: " + resultLi);
if (resultLi > 0) {
Product afterLi = productMapper.selectById(1L);
System.out.println("小李修改后价格: " + afterLi.getPrice() + ", 版本: " + afterLi.getVersion());
}
// 小王修改价格 -30
productWang.setPrice(productWang.getPrice() - 30);
int resultWang = productMapper.updateById(productWang);
System.out.println("小王修改结果: " + resultWang);
if (resultWang == 0) {
// 小王修改失败,需要重新尝试
System.out.println("小王修改失败,版本已变更,需要重新操作");
Product newProductWang = productMapper.selectById(1L);
newProductWang.setPrice(newProductWang.getPrice() - 30);
resultWang = productMapper.updateById(newProductWang);
System.out.println("小王重试结果: " + resultWang);
}
// 最终价格
Product finalProduct = productMapper.selectById(1L);
System.out.println("最终价格: " + finalProduct.getPrice() + ", 版本: " + finalProduct.getVersion());
}
}


实际流程:
1. 小李查询: SELECT id,name,price,version FROM t_product WHERE id=1
结果: price=100, version=02. 小王查询: SELECT id,name,price,version FROM t_product WHERE id=1
结果: price=100, version=03. 小李更新: UPDATE t_product SET name=?, price=150, version=1
WHERE id=1 AND version=0
成功: version变为14. 小王更新: UPDATE t_product SET name=?, price=70, version=1
WHERE id=1 AND version=0
失败: version条件不匹配5. 小王重试: 重新查询 price=150, version=1
更新: SET price=120, version=2 WHERE id=1 AND version=1
成功6. 最终价格: 120元 (正确的价格)
4.悲观锁
悲观锁总是假设最坏的情况,认为共享资源每次被访问的时候就会出现问题(比如共享数据被修改),所以每次在获取资源操作的时候都会上锁,这样其他线程想拿到这个资源就会阻塞直到锁被上一个持有者释放。也就是说,共享资源每次只给一个线程使用,其它线程阻塞,用完后再把资源转让给其它线程。

到此这篇关于MyBatisPlus乐观锁和悲观锁的实现示例的文章就介绍到这了,更多相关MyBatisPlus乐观锁和悲观锁内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
