MyBatis-Plus速成指南:乐观锁

乐观锁:

场景:
  1. 一件商品成本价是 80 元,售价是 100 元。老板先是通知小李把价格增加 50元。小李当时有事情耽搁了一个小时,正好一个小时后,老板觉得 150 太高,又叫小王把商品降低 30 元
  2. 此时,小李和小王同时操作后台系统,小李操作的是偶,系统先取出商品价格 100 元。小王也在操作,取出的价格也是 100 元。小李将价格加了 50 元,并将 150 元存入了数据库。小王将商品减了 30 元,并将 70 存入数据库。
  3. 如果没有锁,小李的操作就完全被小王覆盖了。现在商品价格是 70 元,比成本低 10元。几分钟后,商品售出近千件,老板亏了 1w 多元
乐观锁和悲观锁:
  1. 如果是乐观锁:小王保存价格之前,会检查下价格是否被人修改过。如果被修改过了,则重新取出被修改后的价格,150 元,这样,他会将120 元存入数据库
  2. 如果是悲观锁:小李取出数据后,小王只能等小李操作完之后才能对价格进行操作,也会保证最终的价格是 120 元
模拟修改冲突:
  1. 数据库中增加商品表:

    sql 复制代码
    CREATE TABLE  product
    (
    id BIGINT(20) NOT NULL COMMENT '主键ID',
    name VARCHAR(30) NULL DEFAULT NULL COMMENT '商品名称 ',
    price INT(11) DEFAULT 0 COMMENT '价格 ',
    varsion INT(11) DEFAULT 0 COMMENT '乐观锁版本号 ',
    PRIMARY KEY (id)
    );
  2. 添加数据:

    sql 复制代码
    INSERT INTO product (id, NAME, price)  VALUES (1, '外星人笔记本 ', 100);
  3. 添加实体类:

    java 复制代码
    @Data
    public class Product {
        private Long id;
        private String name;
        private Integer price;
        private Integer version;
    }
  4. 添加 Mapper:

    java 复制代码
    public interface ProductMapper extends BaseMapper<Product> {
    }
  5. 测试:

    java 复制代码
    @Test
    public void testConcurrentUpdate(){
        //小李:
        Product p1 = productMapper.selectById(1L);
        System.out.println("小李取出的价格:" + p1.getPrice());
    
        //小王:
        Product p2 = productMapper.selectById(1L);
        System.out.println("小王取出的价格:" + p2.getPrice());
    
        //小李将价格加了50元,存入了数据库
        p1.setPrice(p1.getPrice()+50);
        int result1 = productMapper.updateById(p1);
        System.out.println("小李修改结果:" + result1);
    
        //小王将商品剪了30元,存入了数据库
        p2.setPrice(p2.getPrice() - 30);
        int result2 = productMapper.updateById(p2);
        System.out.println("小王修改结果:" + result2);
    
        //最后的结果
        Product p3 = productMapper.selectById(1L);
        //价格覆盖,最后的结果:70
        System.out.println("最后的结果:" + p3.getPrice());
    }
乐观锁实现流程:
  1. 数据库添加 version 字段:

  2. 取出记录时候,获取当前 version:

    sql 复制代码
    SELECT id,`name`,price,`version` FROM product WHERE id=1
  3. 更新时 version + 1,如果 where 语句中的 version 版本不对,则更新失败

    sql 复制代码
    update product set price=price+50 ,version = version+1 where id = 1 and  version = 0
MyBatis-Plus 实现乐观锁:
  1. 修改实体类:

    java 复制代码
    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    //@TableName("t_user")
    public class User {
    //    @TableId("u_id")
        private Long id;
    //    @TableField("username")
        private String name;
        private Integer age;
        private String email;
    //    @TableLogic
        private Integer isDelete;
        
        @Version
        private Integer version;
    
    }
  2. 添加乐观锁插件配置:

    java 复制代码
    @Configuration
    @MapperScan("com.springboot.mybatisplus.mapper")
    public class MybatisPlusConfig {
        @Bean
        public MybatisPlusInterceptor mybatisPlusInterceptor(){
            MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
            //添加分页插件
            interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
            //添加乐观锁插件
            interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
            return interceptor;
        }    
    }
  3. 测试修改冲突:

    sql 复制代码
    #小李查询商品信息
    select * from product where id = 1;
    
    #小王查询商品信息:
    select * from product where id = 1;
    
    #小李修改商品价格
    update product set price = price + 50 where id = 1 and version = 0;
    
    #小王修改产品价格,此时version已更新,条件不成立,修改失败
    update product set price = price - 30 where id = 1 and verson = 0;
    
    #最终小王修改失败,查询价格:150
    select * from person where id = 1;
  4. 优化流程:

    java 复制代码
    @Test
    public void testConcurrentUpdate(){
        //乐观锁优化流程
    
        //小李获取数据
        Product p1 = productMapper.selectById(1L);
    
        //小王获取数据
        Product p2 = productMapper.selectById(1L);
    
        //小李修改商品价格 + 50
        p1.setPrice(p1.getPrice() + 50);
        int result1 = productMapper.updateById(p1);
        System.out.println("小李修改的结果" + result1);
    
        //小王修改价格 - 30
        p2.setPrice(p2.getPrice() - 30);
        int result2 = productMapper.updateById(p2);
        if(result2 == 0){
            //修改失败,重新获取 version 并更新
            p2 = productMapper.selectById(1L);
            p2.setPrice(p2.getPrice() - 30);
            result2 = productMapper.updateById(p2);
        }
        System.out.println("小王修改重试的结果" + result2);
    
        //老板查看价格
        Product p3 = productMapper.selectById(1L);
        System.out.println("老板看到的价格:" + p3.getPrice());
    
    }
相关推荐
wljt1 小时前
公司二-面试题
java·开发语言·mybatis
VimorphTech4 小时前
文本已经改了,旧的音频生成结果还能回写吗?
java·数据库·mybatis
麻瓜code9 小时前
双写一致性:为什么是“删缓存“,而不是“改缓存“
java·缓存·mybatis
郭小李1 天前
mybatis 动态 sql
java·sql·mybatis
知守观1 天前
技术考古:iBATIS 2.3.4 时代的持久层,是如何用 Map 玩转 307 个存储过程的?
java·后端·mybatis
captain3761 天前
MyBatis Plus(1)
java·服务器·mybatis
代码不停1 天前
MyBatis进阶
spring boot·mybatis
captain3762 天前
MyBatis(入门5)其他查询操作
java·数据库·spring boot·mybatis
captain3762 天前
MyBatis(进阶1)
mybatis
郭小李2 天前
mybatis的 Mapper.xml语法
xml·java·mybatis