MyBatisPlus樂(lè)觀鎖和悲觀鎖的實(shí)現(xiàn)示例
1.場(chǎng)景
一件商品,成本價(jià)是80元,售價(jià)是100元。老板先是通知小李,說(shuō)你去把商品價(jià)格增加50元。小 李正在玩游戲,耽擱了一個(gè)小時(shí)。正好一個(gè)小時(shí)后,老板覺得商品價(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ù)。是的,如果沒有鎖,小李的操作就 完全被小王的覆蓋了。
現(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)文章
Spring之關(guān)于PropertyDescriptor的擴(kuò)展剖析
這篇文章主要介紹了Spring之關(guān)于PropertyDescriptor的擴(kuò)展剖析,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-07-07
SpringBoot實(shí)現(xiàn)緩存預(yù)熱的幾種常用方案
緩存預(yù)熱是指在 Spring Boot 項(xiàng)目啟動(dòng)時(shí),預(yù)先將數(shù)據(jù)加載到緩存系統(tǒng)(如 Redis)中的一種機(jī)制,本文給大家介紹了SpringBoot實(shí)現(xiàn)緩存預(yù)熱的幾種常用方案,并通過(guò)代碼示例講解的非常詳細(xì),需要的朋友可以參考下2024-02-02
Spring中的@EnableScheduling定時(shí)任務(wù)注解
這篇文章主要介紹了Spring中的@EnableScheduling注解,@EnableScheduling是 Spring Framework 提供的一個(gè)注解,用于啟用 Spring 的定時(shí)任務(wù)功能,通過(guò)使用這個(gè)注解,可以在 Spring 應(yīng)用程序中創(chuàng)建定時(shí)任務(wù),需要的朋友可以參考下2024-01-01
解決mybatis-plus-boot-starter與mybatis-spring-boot-starter的錯(cuò)誤問(wèn)題
本文主要講述了在使用MyBatis和MyBatis-Plus時(shí)遇到的綁定異常問(wèn)題,通過(guò)排查和總結(jié),作者發(fā)現(xiàn)使用MyBatis-Plus?Boot?Starter可以解決這個(gè)問(wèn)題,文章詳細(xì)對(duì)比了MyBatis-Plus?Boot?Starter和MyBatis?Spring?Boot?Starter的功能和使用場(chǎng)景2025-01-01
Java動(dòng)態(tài)規(guī)劃之編輯距離問(wèn)題示例代碼
這篇文章主要介紹了Java動(dòng)態(tài)規(guī)劃之編輯距離問(wèn)題示例代碼,具有一定參考價(jià)值,需要的朋友可以了解下。2017-11-11
通過(guò)自定制LogManager實(shí)現(xiàn)程序完全自定義的logger
本章主要闡述怎么完全定制化LogManager來(lái)實(shí)現(xiàn)應(yīng)用程序完全自定制的logger,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-03-03
SpringBoot集成Mybatis的實(shí)現(xiàn)步驟
這篇文章主要介紹了SpringBoot集成Mybatis的實(shí)現(xiàn)步驟,本文通過(guò)SpringBoot +MyBatis 實(shí)現(xiàn)對(duì)數(shù)據(jù)庫(kù)學(xué)生表的查詢操作,需要的朋友可以參考下2020-12-12
基于JavaMail的Java實(shí)現(xiàn)簡(jiǎn)單郵件發(fā)送功能
這篇文章主要為大家詳細(xì)介紹了基于JavaMail的Java實(shí)現(xiàn)簡(jiǎn)單郵件發(fā)送功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-09-09
基于java開發(fā)之系統(tǒng)托盤的應(yīng)用
本篇文章介紹了,基于java開發(fā)之系統(tǒng)托盤的應(yīng)用。需要的朋友參考下2013-05-05

