一件商品,成本价是
80
元,售价是
100
元。老板先是通知小李,说你去把商品价格增加
50
元。小
李正在玩游戏,耽搁了一个小时。正好一个小时后,老板觉得商品价格增加到
150
元,价格太
高,可能会影响销量。又通知小王,你把商品价格降低
30
元。
此时,小李和小王同时操作商品后台系统。小李操作的时候,系统先取出商品价格
100
元;小王
也在操作,取出的商品价格也是
100
元。小李将价格加了
50
元,并将
100+50=150
元存入了数据
库;小王将商品减了
30
元,并将
100-30=70
元存入了数据库。是的,如果没有锁,小李的操作就
完全被小王的覆盖了。
现在商品价格是
70
元,比成本价低
10
元。几分钟后,这个商品很快出售了
1
千多件商品,老板亏
1
万多。
上面的故事,如果是乐观锁,小王保存价格前,会检查下价格是否被人修改过了。如果被修改过
了,则重新取出的被修改后的价格,
150
元,这样他会将
120
元存入数据库。
如果是悲观锁,小李取出数据后,小王只能等小李操作完之后,才能对价格进行操作,也会保证
最终的价格是
120
元。
使用version控制
需要在配置类中设置乐观锁插件
@Configuration @MapperScan("com.example.demos.mapper") public class MybatisPlusConfig { @Bean public MybatisPlusInterceptor mybatisPlusInterceptor(){ MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor(); //添加分页插件 mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); //添加乐观锁插件 mybatisPlusInterceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor()); return mybatisPlusInterceptor; } }
首先是实体类
@Data public class Product { private Long id; private String name; private Integer price; @Version//用来标识乐观锁版本号字段 private Integer version; }
然后是mapper层
@Repository public interface ProductMapper extends BaseMapper<Product> { }
测试
//测试乐观锁 @Autowired private ProductMapper productMapper; @Test public void testVersionUpdate(){ //小李取数据 Product productLi = productMapper.selectById(1L);//查询版本是0 //小王取数据 Product productWang = productMapper.selectById(1L);//查询版本是0 //小李修改+50 productLi.setPrice(productLi.getPrice()+50); int result1 = productMapper.updateById(productLi); System.out.println("小李修改的结果"+result1); //小王修改-30 /* * 这个时候小李已经修改了,版本号会变成1,小王修改因为版本号不同无法修改,会修改失败(小王这个时候的版本号是0) * 失败result2会是0 * */ productWang.setPrice(productWang.getPrice()-30); int result2 = productMapper.updateById(productWang); if(result2==0){ //修改失败,重新取得version并更新 productWang = productMapper.selectById(1L);//这个时候查询的版本号是1 productWang.setPrice(productWang.getPrice()-30); result2 = productMapper.updateById(productWang); } System.out.println("小王修改重试的结果"+result2); //老板看价格 Product p3 = productMapper.selectById(1L); System.out.println("老板看价格:" + p3.getPrice()); }