MyBatisPlus樂(lè)觀鎖和悲觀鎖的實(shí)現(xiàn)示例
1.場(chǎng)景
一件商品,成本價(jià)是80元,售價(jià)是100元。老板先是通知小李,說(shuō)你去把商品價(jià)格增加50元。小 李正在玩游戲,耽擱了一個(gè)小時(shí)。正好一個(gè)小時(shí)后,老板覺(jué)得商品價(jià)格增加到150元,價(jià)格太高,可能會(huì)影響銷量。又通知小王,你把商品價(jià)格降低30元。
此時(shí),小李和小王同時(shí)操作商品后臺(tái)系統(tǒng)。小李操作的時(shí)候,系統(tǒng)先取出商品價(jià)格100元;小王 也在操作,取出的商品價(jià)格也是100元。小李將價(jià)格加了50元,并將100+50=150元存入了數(shù)據(jù) 庫(kù);小王將商品減了30元,并將100-30=70元存入了數(shù)據(jù)庫(kù)。是的,如果沒(méi)有鎖,小李的操作就 完全被小王的覆蓋了。
現(xiàn)在商品價(jià)格是70元,比成本價(jià)低10元。幾分鐘后,這個(gè)商品很快出售了1千多件商品,老板虧1 萬(wàn)多。
2.樂(lè)觀鎖和悲觀鎖
上面的故事,如果是樂(lè)觀鎖,小王保存價(jià)格前,會(huì)檢查下價(jià)格是否被人修改過(guò)了。如果被修改過(guò) 了,則重新取出的被修改后的價(jià)格, 150元,這樣他會(huì)將120元存入數(shù)據(jù)庫(kù)。
如果是悲觀鎖,小李取出數(shù)據(jù)后,小王只能等小李操作完之后,才能對(duì)價(jià)格進(jìn)行操作,也會(huì)保證 最終的價(jià)格是120元。
3.樂(lè)觀鎖實(shí)現(xiàn)
添加插件:
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
// 添加分頁(yè)插件
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
// 添加樂(lè)觀鎖插件
interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
return interceptor;
}創(chuàng)建表:
CREATE TABLE t_product
(
id BIGINT(20) NOT NULL COMMENT '主鍵ID',
NAME VARCHAR(30) NULL DEFAULT NULL COMMENT '商品名稱 ',
price INT(11) DEFAULT 0 COMMENT '價(jià)格 ',
VERSION INT(11) DEFAULT 0 COMMENT '樂(lè)觀鎖版本號(hào) ',
PRIMARY KEY (id)
);
添加數(shù)據(jù):
INSERT INTO t_product (id, NAME, price) VALUES (1, '外星人筆記本 ', 100);
如圖:

編寫實(shí)體類:
package com.qcby.entity;
import com.baomidou.mybatisplus.annotation.Version;
import lombok.Data;
@Data
public class Product {
private Long id;
private String name;
private Integer price;
@Version
private Integer version;
}mapper:
package com.qcby.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.qcby.entity.Product;
import org.springframework.stereotype.Repository;
@Repository
public interface ProductMapper extends BaseMapper<Product> {
}
測(cè)試:
package com.qcby;
import com.qcby.entity.Product;
import com.qcby.mapper.ProductMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
public class MybatisTestOptimisticLock {
@Autowired
private ProductMapper productMapper;
@Test
public void testOptimisticLock() {
// 小李查詢商品信息
Product productLi = productMapper.selectById(1L);
System.out.println("小李查詢價(jià)格: " + productLi.getPrice() + ", 版本: " + productLi.getVersion());
// 小王查詢商品信息
Product productWang = productMapper.selectById(1L);
System.out.println("小王查詢價(jià)格: " + productWang.getPrice() + ", 版本: " + productWang.getVersion());
// 小李修改價(jià)格 +50
productLi.setPrice(productLi.getPrice() + 50);
int resultLi = productMapper.updateById(productLi);
System.out.println("小李修改結(jié)果: " + resultLi);
if (resultLi > 0) {
Product afterLi = productMapper.selectById(1L);
System.out.println("小李修改后價(jià)格: " + afterLi.getPrice() + ", 版本: " + afterLi.getVersion());
}
// 小王修改價(jià)格 -30
productWang.setPrice(productWang.getPrice() - 30);
int resultWang = productMapper.updateById(productWang);
System.out.println("小王修改結(jié)果: " + resultWang);
if (resultWang == 0) {
// 小王修改失敗,需要重新嘗試
System.out.println("小王修改失敗,版本已變更,需要重新操作");
Product newProductWang = productMapper.selectById(1L);
newProductWang.setPrice(newProductWang.getPrice() - 30);
resultWang = productMapper.updateById(newProductWang);
System.out.println("小王重試結(jié)果: " + resultWang);
}
// 最終價(jià)格
Product finalProduct = productMapper.selectById(1L);
System.out.println("最終價(jià)格: " + finalProduct.getPrice() + ", 版本: " + finalProduct.getVersion());
}
}


實(shí)際流程:
1. 小李查詢: SELECT id,name,price,version FROM t_product WHERE id=1
結(jié)果: price=100, version=02. 小王查詢: SELECT id,name,price,version FROM t_product WHERE id=1
結(jié)果: price=100, version=03. 小李更新: UPDATE t_product SET name=?, price=150, version=1
WHERE id=1 AND version=0
成功: version變?yōu)?4. 小王更新: UPDATE t_product SET name=?, price=70, version=1
WHERE id=1 AND version=0
失敗: version條件不匹配5. 小王重試: 重新查詢 price=150, version=1
更新: SET price=120, version=2 WHERE id=1 AND version=1
成功6. 最終價(jià)格: 120元 (正確的價(jià)格)
4.悲觀鎖
悲觀鎖總是假設(shè)最壞的情況,認(rèn)為共享資源每次被訪問(wèn)的時(shí)候就會(huì)出現(xiàn)問(wèn)題(比如共享數(shù)據(jù)被修改),所以每次在獲取資源操作的時(shí)候都會(huì)上鎖,這樣其他線程想拿到這個(gè)資源就會(huì)阻塞直到鎖被上一個(gè)持有者釋放。也就是說(shuō),共享資源每次只給一個(gè)線程使用,其它線程阻塞,用完后再把資源轉(zhuǎn)讓給其它線程。

到此這篇關(guān)于MyBatisPlus樂(lè)觀鎖和悲觀鎖的實(shí)現(xiàn)示例的文章就介紹到這了,更多相關(guān)MyBatisPlus樂(lè)觀鎖和悲觀鎖內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
jvm crash的崩潰日志詳細(xì)分析及注意點(diǎn)
本篇文章主要介紹了jvm crash的崩潰日志詳細(xì)分析及注意點(diǎn)。具有很好的參考價(jià)值,下面跟著小編一起來(lái)看下吧2017-04-04
使用SpringSecurity設(shè)置角色和權(quán)限的注意點(diǎn)
這篇文章主要介紹了使用SpringSecurity設(shè)置角色和權(quán)限的注意點(diǎn),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-03-03
通過(guò)原理解析Spring mvc的內(nèi)置編碼過(guò)濾器
這篇文章通過(guò)原理主要給大家解析了關(guān)于Spring mvc的內(nèi)置編碼過(guò)濾器的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起看看吧。2017-09-09
使用springboot對(duì)外部靜態(tài)資源文件的處理操作
這篇文章主要介紹了使用springboot對(duì)外部靜態(tài)資源文件的處理操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-08-08
詳解SpringBoot如何實(shí)現(xiàn)統(tǒng)一后端返回格式
在前后端分離的項(xiàng)目中后端返回的格式一定要友好,不然會(huì)對(duì)前端的開發(fā)人員帶來(lái)很多的工作量。那么SpringBoot如何做到統(tǒng)一的后端返回格式呢?本文將為大家詳細(xì)講講2022-04-04
Java高級(jí)之HashMap中的entrySet()方法使用
這篇文章主要介紹了Java高級(jí)之HashMap中的entrySet()方法使用,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-03-03
解決Java?properties文件里面如何寫"\"的問(wèn)題
由于properties使用“\”相當(dāng)于是java的轉(zhuǎn)義符,如果想要寫出\的效果,只需修改相應(yīng)的寫法即可,對(duì)java?properties文件里的"\"寫法感興趣的朋友一起看看吧2022-04-04
詳解SpringMVC的類型轉(zhuǎn)換及驗(yàn)證方法
在本篇文章里面我們給大家詳細(xì)分析了SpringMVC的類型轉(zhuǎn)換及驗(yàn)證方法的相關(guān)知識(shí),對(duì)此有需要的朋友們學(xué)習(xí)下吧。2018-10-10

