首頁 > 軟體

詳解MybatisPlus中@Version註解的使用

2022-06-16 18:01:20

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_idnamesexageversion
1測試00

執行上面程式,資料庫記錄如下:

user_idnamesexageversion
1測試016

1.上面程式碼將執行10次迴圈操作,每次操作啟動兩個執行緒(執行緒 A 和 執行緒 B)去修改使用者資料。

2.如果資料沒有任何衝突,則使用者的年齡應該是 20。但是上面程式執行完成後年齡為 16

3.這就說明,線上程執行的時候,可能A 剛好修改了version,並沒有執行完,就到B執行緒了,就導致B執行緒修改失敗

以上就是詳解MybatisPlus中@Version註解的使用的詳細內容,更多關於MybatisPlus @Version註解的資料請關注it145.com其它相關文章!


IT145.com E-mail:sddin#qq.com