詳解MybatisPlus中@Version註解的使用
1. 簡單介紹
嗨,大傢好,今天給想給大傢分享一下關於Mybatis-plus 的 Service 層的一些方法的使用。今天沒有總結,因為都是一些API沒有什麼可以總結的,直接看著調用就可以瞭。
下面我們將介紹 @Version 註解的用法,以及每個屬性的實際意義和用法
2. 註解說明
在 MyBatis Plus 中,使用 @Version 實現樂觀鎖,該註解用於字段上面
3. 什麼是樂觀鎖
3.1 樂觀鎖簡介
- 樂觀鎖(Optimistic Locking)是相對悲觀鎖而言的,樂觀鎖假設數據一般情況下不會造成沖突
- 所以在數據進行提交更新的時候,才會正式對數據的沖突進行檢測
- 如果發現沖突瞭,則返回給用戶錯誤的信息,讓用戶決定如何去做
- 樂觀鎖適用於讀操作多的場景,這樣可以提高程序的吞吐量
3.2 樂觀鎖實例
存在兩個線程 A 和 B,分別從數據庫讀取數據。執行後,線程 A 和 線程 B 的 version 均等於 1。如下圖
線程 A 處理完業務,提交數據。此時,數據庫中該記錄的 version 為 2。如下圖:
線程 B 也處理完業務瞭,提交數據。此時,數據庫中的 version 已經等於 2,而線程的 version 還是 1。程序給出錯誤信息,不允許線程 B 操作數據。如下圖:
- 樂觀鎖機制采取瞭更加寬松的加鎖機制
- 樂觀鎖是相對悲觀鎖而言,也是為瞭避免數據庫幻讀、業務處理時間過長等原因引起數據處理錯誤的一種機制
- 但樂觀鎖不會刻意使用數據庫本身的鎖機制,而是依據數據本身來保證數據的正確性
4. 實例代碼
本實例將在前面用到的 user 表上面進行。在進行之前,現在 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 的樂觀鎖插件,該插件會自動幫我們將 version 加一操作
註意,這裡和分頁操作一樣,需要進行配置,如果不配置,@Version是不會生效的
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; } }
測試樂觀鎖代碼,我們創建兩個線程 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 { // 重置數據 AnnotationUser5Bean user5Bean = new AnnotationUser5Bean(); user5Bean.setUserId(1); user5Bean.setAge(0); user5Bean.setVersion(0); userMapper.updateById(user5Bean); // 修改數據 for (int i = 0; i < 10; i++) { System.out.println("第 " + (i + 1) + " 次修改數據"); 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(); } }
在運行上面代碼之前,我們數據庫中的記錄值如下:
user_id | name | sex | age | version |
---|---|---|---|---|
1 | 測試 | 男 | 0 | 0 |
運行上面程序,數據庫記錄如下:
user_id | name | sex | age | version |
---|---|---|---|---|
1 | 測試 | 男 | 0 | 16 |
1.上面代碼將執行10次循環操作,每次操作啟動兩個線程(線程 A 和 線程 B)去修改用戶數據。
2.如果數據沒有任何沖突,則用戶的年齡應該是 20。但是上面程序運行完成後年齡為 16
3.這就說明,在線程運行的時候,可能A 剛好修改瞭version,並沒有執行完,就到B線程瞭,就導致B線程修改失敗
以上就是詳解MybatisPlus中@Version註解的使用的詳細內容,更多關於MybatisPlus @Version註解的資料請關註WalkonNet其它相關文章!
推薦閱讀:
- MyBatisPlus利用Service實現獲取數據列表
- mybatis plus樂觀鎖及實現詳解
- mybatis-plus(insertBatchSomeColumn批量添加方式)
- SpringBoot整合Mybatis Plus多數據源的實現示例
- 詳解MybatisPlus3.4版本之後分頁插件的使用