參考博文
https://baijiahao.baidu.com/s?id=1659469738216922362&wfr=spider&for=pc
1 沒有鎖的風險
開發不設鎖,就像沒穿褲衩。當海水退去,程式的漏洞就會被一覽無遺。現在沒出問題,隻是因為還有海水為你遮擋了老闆們的視線。
聽小編給你講個朋友的故事:一件商品,成本價是80元,售價是100元。老闆先是通知小李,說你去把商品價格增加50元。小李正在玩遊戲,耽擱了一個小時。正好一個小時後,老闆覺得商品價格增加到150元,價格太高,可能會影響銷量。又通知小王,你把商品價格降低30元。
此時,小李和小王同時操作商品背景系統。小李操作的時候,系統先取出商品價格100元;小王也在操作,取出的商品價格也是100元。小李将價格加了50元,并将100+50=150元存入了資料庫;小王将商品減了30元,并将100-30=70元存入了資料庫。是的,如果沒有鎖,小李的操作就完全被小王的覆寫了。
現在商品價格是70元,比成本價低10元。幾分鐘後,這個商品很快出售了1千多件商品,老闆虧1多萬。
2 樂觀鎖與悲觀鎖
上面的故事,如果是樂觀鎖,小王儲存價格前,會檢查下價格是否被人修改過了。如果被修改過了,則重新取出的被修改後的價格,150元,這樣他會将120元存入資料庫。如果是悲觀鎖,小李取出資料後,小王隻能等小李操作完之後,才能對價格進行操作,也會保證最終的價格是120元。
接下來小編将介紹如何在mybatis-plus項目中,添加樂觀鎖。
需求:是否能夠確定小李先操作的價格不會被小王後操作而覆寫,對應的版本号是否有自動增加。
注意
- 樂觀鎖資料類型支援int、integer、long、timestamp
- 僅支援updateById和update方法
1 搭建 springboot 整合 mybatis-plus環境,可參考博文
https://blog.csdn.net/qq_41712271/article/details/115756865
2 MybatisPlusPageConfig 配置類修改
package cn.huawei.guanggao.config;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MybatisPlusPageConfig {
/* 舊版本配置
@Bean
public PaginationInterceptor paginationInterceptor(){
return new PaginationInterceptor();
}*/
/**
* 新的分頁插件 ,攔截器的原理感覺
*/
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
//樂觀鎖插件
interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
return interceptor;
}
}
3 建立一個商品類,字段為:id、name、price、version;這裡請注意,必須給version字段加上@Version的注解
CREATE TABLE goods
(
id INT AUTO_INCREMENT COMMENT '商品id'
PRIMARY KEY,
NAME VARCHAR(30) NULL COMMENT '商品名稱',
stock INT DEFAULT 0 NULL COMMENT '商品庫存',
VERSION INT DEFAULT 0 NULL COMMENT '并發版本控制'
)
COMMENT '商品表' CHARSET = utf8;
package cn.huawei.guanggao.pojo;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.annotation.Version;
import lombok.Data;
import java.io.Serializable;
@Data
@TableName("goods")
public class Goods implements Serializable {
private static final long serialVersionUID = -1939317434177299884L;
@TableId(value = "id", type = IdType.AUTO)
private Integer id;
private String name;
private Integer stock;
@Version
private Integer version;
}
4 測試類
@Autowired
private GoodsMapper goodsMapper;
@Test
void test_23() {
//SELECT id,name,stock,version FROM goods WHERE id=2
//id, name, stock, version
//2, 水果, 100, 0
Goods good1 = goodsMapper.selectById(2);
//SELECT id,name,stock,version FROM goods WHERE id=2
//id, name, stock, version
//2, 水果, 100, 0
Goods good2 = goodsMapper.selectById(2);
//小李把把價格 +50
good1.setStock(good1.getStock() + 50);
//UPDATE goods SET name=水果, stock=150, version=1 WHERE id=2 AND version=0
int i = goodsMapper.updateById(good1);
System.out.println("###################"+i);
//################### 1
//以上修改成功
//小王把價格 -30
good2.setStock(good2.getStock() - 30);
int result = goodsMapper.updateById(good2);
//UPDATE goods SET name=水果, stock=70, version=1 WHERE id=2 AND version=0
//此操作會會失敗,因為上面的加100操作成功後,version的版本已經是1
System.out.println("-----------------"+result);
//-----------------0
//是以需要這樣來操作
if (result == 0) {
//SELECT id,name,stock,version FROM goods WHERE id=2
//id, name, stock, version
//2, 水果, 150, 1
Goods p2 = goodsMapper.selectById(2);
//UPDATE goods SET name=水果, stock=120, version=2 WHERE id=2 AND version=1
p2.setStock(p2.getStock() - 30);
goodsMapper.updateById(p2);
}
//最後的結果
//SELECT id,name,stock,version FROM goods WHERE id=2
//id, name, stock, version
//2, 水果, 120, 2
Goods goods3 = goodsMapper.selectById(2L);
System.out.println("最後的結果:" + goods3.getStock());
}