MybatisPlus:乐观锁

  1. 准备工作
  2. 乐观锁实现流程
  3. Mybatis-Plus实现乐观锁
    1. 添加乐观锁插件配置
    2. 在实体类上添加@Version注解
    3. 实验

准备工作

准备数据

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);

实体类

@Data
public class Product {
    private Long id;
    private String name;
    private Integer price;
    private Integer version;
}

mapper

public interface ProductMapper extends BaseMapper<Product> {
}

乐观锁实现流程

数据库中添加version字段

取出记录时,获取当前version

SELECT id,`name`,price,`version` FROM product WHERE id=1

更新时,version + 1,如果where语句中的version版本不对,则更新失败

UPDATE product SET price=price+50, `version`=`version` + 1 WHERE id=1 AND `version`=1

Mybatis-Plus实现乐观锁

添加乐观锁插件配置

@Configuration
@MapperScan("com.lin.mymybatisplusdemo.mapper") //可以将主类中的注解移到此处
public class MybatisPlusConfig {
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        // MybatisPlusInterceptor : mybatis-plus 的拦截器
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();

        //添加乐观锁插件
        interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
        
        // 添加内部拦截器 : // 如果配置多个插件, 切记分页最后添加
        // 如果有多数据源可以不配具体类型, 否则都建议配上具体的 DbType
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return interceptor;
    }
}

在实体类上添加@Version注解

@Data
public class Product {
    private Long id;
    private String name;
    private Integer price;
    @Version
    private Integer version;
}

实验

/**
* 场景:
*  一件商品,成本价是80元,售价是100元。老板先是通知小李,说你去把商品价格增加50元。小
* 李正在玩游戏,耽搁了一个小时。正好一个小时后,老板觉得商品价格增加到150元,价格太
* 高,可能会影响销量。又通知小王,你把商品价格降低30元。
*
* */
@Autowired
ProductMapper productMapper;
@Test
public void testConcurrentVersionUpdate() {
    //小李取数据:SELECT id,name,price,version FROM t_product WHERE id=? ==> Parameters: 1(Long)
    Product p1 = productMapper.selectById(1L);
    //小王取数据:SELECT id,name,price,version FROM t_product WHERE id=? ==> Parameters: 1(Long)
    Product p2 = productMapper.selectById(1L);
    //小李修改 + 50
    p1.setPrice(p1.getPrice() + 50);
    // ==>  Preparing: UPDATE t_product SET name=?, price=?, version=? WHERE id=? AND version=?
    //==> Parameters: 外星人笔记本(String), 150(Integer), 1(Integer), 1(Long), 0(Integer)
    int result1 = productMapper.updateById(p1);
    System.out.println("小李修改的结果:" + result1);// 小李修改的结果:1
    //小王修改 - 30
    p2.setPrice(p2.getPrice() - 30);
    //==>  Preparing: UPDATE t_product SET name=?, price=?, version=? WHERE id=? AND version=?
    //==> Parameters: 外星人笔记本(String), 70(Integer), 1(Integer), 1(Long), 0(Integer)
    int result2 = productMapper.updateById(p2);
    // 小王修改的结果:0 ===> 这个时候数据库里面的version=1,但是小王的version=0
    System.out.println("小王修改的结果:" + result2);
    if(result2 == 0){
        //失败重试,重新获取version并更新
        // ==>  Preparing: SELECT id,name,price,version FROM t_product WHERE id=?
        //==> Parameters: 1(Long)
        p2 = productMapper.selectById(1L);
        p2.setPrice(p2.getPrice() - 30);
        // ==>  Preparing: UPDATE t_product SET name=?, price=?, version=? WHERE id=? AND version=?
        //==> Parameters: 外星人笔记本(String), 120(Integer), 2(Integer), 1(Long), 1(Integer)
        result2 = productMapper.updateById(p2);
    }
    // 小王修改重试的结果:1
    System.out.println("小王修改重试的结果:" + result2);
    //老板看价格
    Product p3 = productMapper.selectById(1L);
    //老板看价格:120
    System.out.println("老板看价格:" + p3.getPrice());
}

转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 1909773034@qq.com

×

喜欢就点赞,疼爱就打赏