准备工作
准备数据
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