詳解MybatisPlus中@Version注解的使用
1. 簡(jiǎn)單介紹
嗨,大家好,今天給想給大家分享一下關(guān)于Mybatis-plus 的 Service 層的一些方法的使用。今天沒有總結(jié),因?yàn)槎际且恍〢PI沒有什么可以總結(jié)的,直接看著調(diào)用就可以了。
下面我們將介紹 @Version 注解的用法,以及每個(gè)屬性的實(shí)際意義和用法
2. 注解說(shuō)明
在 MyBatis Plus 中,使用 @Version 實(shí)現(xiàn)樂觀鎖,該注解用于字段上面
3. 什么是樂觀鎖
3.1 樂觀鎖簡(jiǎn)介
- 樂觀鎖(Optimistic Locking)是相對(duì)悲觀鎖而言的,樂觀鎖假設(shè)數(shù)據(jù)一般情況下不會(huì)造成沖突
- 所以在數(shù)據(jù)進(jìn)行提交更新的時(shí)候,才會(huì)正式對(duì)數(shù)據(jù)的沖突進(jìn)行檢測(cè)
- 如果發(fā)現(xiàn)沖突了,則返回給用戶錯(cuò)誤的信息,讓用戶決定如何去做
- 樂觀鎖適用于讀操作多的場(chǎng)景,這樣可以提高程序的吞吐量
3.2 樂觀鎖實(shí)例
存在兩個(gè)線程 A 和 B,分別從數(shù)據(jù)庫(kù)讀取數(shù)據(jù)。執(zhí)行后,線程 A 和 線程 B 的 version 均等于 1。如下圖
線程 A 處理完業(yè)務(wù),提交數(shù)據(jù)。此時(shí),數(shù)據(jù)庫(kù)中該記錄的 version 為 2。如下圖:
線程 B 也處理完業(yè)務(wù)了,提交數(shù)據(jù)。此時(shí),數(shù)據(jù)庫(kù)中的 version 已經(jīng)等于 2,而線程的 version 還是 1。程序給出錯(cuò)誤信息,不允許線程 B 操作數(shù)據(jù)。如下圖:
- 樂觀鎖機(jī)制采取了更加寬松的加鎖機(jī)制
- 樂觀鎖是相對(duì)悲觀鎖而言,也是為了避免數(shù)據(jù)庫(kù)幻讀、業(yè)務(wù)處理時(shí)間過長(zhǎng)等原因引起數(shù)據(jù)處理錯(cuò)誤的一種機(jī)制
- 但樂觀鎖不會(huì)刻意使用數(shù)據(jù)庫(kù)本身的鎖機(jī)制,而是依據(jù)數(shù)據(jù)本身來(lái)保證數(shù)據(jù)的正確性
4. 實(shí)例代碼
本實(shí)例將在前面用到的 user 表上面進(jìn)行。在進(jìn)行之前,現(xiàn)在 user 表中添加 version 字段
ALTER TABLE `user` ADD COLUMN `version` int UNSIGNED NULL COMMENT '版本信息';
:::info
定義 user 表的 JavaBean,代碼如下:
import com.baomidou.mybatisplus.annotation.*; @TableName(value = "user") public class AnnotationUser5Bean { @TableId(value = "user_id", type = IdType.AUTO) private String userId; @TableField("name") private String name; @TableField("sex") private String sex; @TableField("age") private Integer age; @Version private int version; // 忽略 getter 和 setter 方法 }
添加 MyBatis Plus 的樂觀鎖插件,該插件會(huì)自動(dòng)幫我們將 version 加一操作
注意,這里和分頁(yè)操作一樣,需要進(jìn)行配置,如果不配置,@Version是不會(huì)生效的
import com.baomidou.mybatisplus.annotation.DbType; import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class MybatisPlusConfig { @Bean public MybatisPlusInterceptor paginationInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); // 樂觀鎖插件 interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor()); return interceptor; } }
測(cè)試樂觀鎖代碼,我們創(chuàng)建兩個(gè)線程 A 和 B 分別去修改用戶ID為 1 的用戶年齡,然后觀察年齡和version字段的值
package com.hxstrive.mybatis_plus.simple_mapper.annotation; import com.hxstrive.mybatis_plus.mapper.AnnotationUser5Mapper; import com.hxstrive.mybatis_plus.model.AnnotationUser5Bean; import org.junit.jupiter.api.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import java.util.concurrent.CountDownLatch; @RunWith(SpringRunner.class) @SpringBootTest class AnnotationDemo5 { @Autowired private AnnotationUser5Mapper userMapper; @Test void contextLoads() throws Exception { // 重置數(shù)據(jù) AnnotationUser5Bean user5Bean = new AnnotationUser5Bean(); user5Bean.setUserId(1); user5Bean.setAge(0); user5Bean.setVersion(0); userMapper.updateById(user5Bean); // 修改數(shù)據(jù) for (int i = 0; i < 10; i++) { System.out.println("第 " + (i + 1) + " 次修改數(shù)據(jù)"); final CountDownLatch countDownLatch = new CountDownLatch(2); modifyUser(countDownLatch, "My-Thread-A", 1); modifyUser(countDownLatch, "My-Thread-B", 1); countDownLatch.await(); Thread.sleep(100L); } } private void modifyUser(final CountDownLatch countDownLatch, String threadName, int userId) { Thread t = new Thread(new Runnable() { @Override public void run() { try { String threadName = Thread.currentThread().getName(); try { AnnotationUser5Bean userBean = userMapper.selectById(userId); if (null == userBean) { return; } AnnotationUser5Bean newBean = new AnnotationUser5Bean(); newBean.setName(userBean.getName()); newBean.setSex(userBean.getSex()); newBean.setAge(userBean.getAge() + 1); newBean.setUserId(userBean.getUserId()); newBean.setVersion(userBean.getVersion()); int result = userMapper.updateById(newBean); System.out.println("result=" + result + " ==> " + userBean); } catch (Exception e) { System.err.println(threadName + " " + e.getMessage()); } } finally { countDownLatch.countDown(); } } }); t.setName(threadName); t.start(); } }
在運(yùn)行上面代碼之前,我們數(shù)據(jù)庫(kù)中的記錄值如下:
user_id | name | sex | age | version |
---|---|---|---|---|
1 | 測(cè)試 | 男 | 0 | 0 |
運(yùn)行上面程序,數(shù)據(jù)庫(kù)記錄如下:
user_id | name | sex | age | version |
---|---|---|---|---|
1 | 測(cè)試 | 男 | 0 | 16 |
1.上面代碼將執(zhí)行10次循環(huán)操作,每次操作啟動(dòng)兩個(gè)線程(線程 A 和 線程 B)去修改用戶數(shù)據(jù)。
2.如果數(shù)據(jù)沒有任何沖突,則用戶的年齡應(yīng)該是 20。但是上面程序運(yùn)行完成后年齡為 16
3.這就說(shuō)明,在線程運(yùn)行的時(shí)候,可能A 剛好修改了version,并沒有執(zhí)行完,就到B線程了,就導(dǎo)致B線程修改失敗
以上就是詳解MybatisPlus中@Version注解的使用的詳細(xì)內(nèi)容,更多關(guān)于MybatisPlus @Version注解的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
詳解SpringBoot 快速整合MyBatis(去XML化)
本篇文章主要介紹了詳解SpringBoot 快速整合MyBatis(去XML化),非常具有實(shí)用價(jià)值,需要的朋友可以參考下2017-10-10Spring實(shí)戰(zhàn)之Bean的后處理器操作示例
這篇文章主要介紹了Spring實(shí)戰(zhàn)之Bean的后處理器操作,結(jié)合實(shí)例形式詳細(xì)分析了Bean的后處理器相關(guān)配置、操作方法及使用注意事項(xiàng),需要的朋友可以參考下2019-12-12IDEA導(dǎo)入jar包的完整實(shí)現(xiàn)步驟
由于導(dǎo)入jar包項(xiàng)目存在很多不確定的問題,導(dǎo)致每次都需要調(diào)試、配置好多遍,對(duì)此特意記錄下來(lái),這篇文章主要給大家介紹了關(guān)于IDEA導(dǎo)入jar包的相關(guān)資料,需要的朋友可以參考下2024-01-01SpringBoot+RabbitMq具體使用的幾種姿勢(shì)
這篇文章主要介紹了SpringBoot+RabbitMq具體使用的幾種姿勢(shì),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-05-05jpa使用manyToOne(opntional=true)踩過的坑及解決
這篇文章主要介紹了jpa使用manyToOne(opntional=true)踩過的坑及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-10-10解決springboot啟動(dòng)成功,但訪問404的問題
這篇文章主要介紹了解決springboot啟動(dòng)成功,但訪問404的問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-07-07