溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶(hù)服務(wù)條款》

MybatisPlus中@Version注解如何使用

發(fā)布時(shí)間:2022-06-17 09:12:01 來(lái)源:億速云 閱讀:157 作者:zzz 欄目:開(kāi)發(fā)技術(shù)

這篇文章主要講解了“MybatisPlus中@Version注解如何使用”,文中的講解內(nèi)容簡(jiǎn)單清晰,易于學(xué)習(xí)與理解,下面請(qǐng)大家跟著小編的思路慢慢深入,一起來(lái)研究和學(xué)習(xí)“MybatisPlus中@Version注解如何使用”吧!

    注解說(shuō)明

    在 MyBatis Plus 中,使用 @Version 實(shí)現(xiàn)樂(lè)觀鎖,該注解用于字段上面

    什么是樂(lè)觀鎖

    1 樂(lè)觀鎖簡(jiǎn)介

    • 樂(lè)觀鎖(Optimistic Locking)是相對(duì)悲觀鎖而言的,樂(lè)觀鎖假設(shè)數(shù)據(jù)一般情況下不會(huì)造成沖突

    • 所以在數(shù)據(jù)進(jìn)行提交更新的時(shí)候,才會(huì)正式對(duì)數(shù)據(jù)的沖突進(jìn)行檢測(cè)

    • 如果發(fā)現(xiàn)沖突了,則返回給用戶(hù)錯(cuò)誤的信息,讓用戶(hù)決定如何去做

    • 樂(lè)觀鎖適用于讀操作多的場(chǎng)景,這樣可以提高程序的吞吐量

    2 樂(lè)觀鎖實(shí)例

    存在兩個(gè)線(xiàn)程 A 和 B,分別從數(shù)據(jù)庫(kù)讀取數(shù)據(jù)。執(zhí)行后,線(xiàn)程 A 和 線(xiàn)程 B 的 version 均等于 1。如下圖

    MybatisPlus中@Version注解如何使用

    線(xiàn)程 A 處理完業(yè)務(wù),提交數(shù)據(jù)。此時(shí),數(shù)據(jù)庫(kù)中該記錄的 version 為 2。如下圖:

    MybatisPlus中@Version注解如何使用

    線(xiàn)程 B 也處理完業(yè)務(wù)了,提交數(shù)據(jù)。此時(shí),數(shù)據(jù)庫(kù)中的 version 已經(jīng)等于 2,而線(xiàn)程的 version 還是 1。程序給出錯(cuò)誤信息,不允許線(xiàn)程 B 操作數(shù)據(jù)。如下圖:

    MybatisPlus中@Version注解如何使用

    • 樂(lè)觀鎖機(jī)制采取了更加寬松的加鎖機(jī)制

    • 樂(lè)觀鎖是相對(duì)悲觀鎖而言,也是為了避免數(shù)據(jù)庫(kù)幻讀、業(yè)務(wù)處理時(shí)間過(guò)長(zhǎng)等原因引起數(shù)據(jù)處理錯(cuò)誤的一種機(jī)制

    • 但樂(lè)觀鎖不會(huì)刻意使用數(shù)據(jù)庫(kù)本身的鎖機(jī)制,而是依據(jù)數(shù)據(jù)本身來(lái)保證數(shù)據(jù)的正確性

    實(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 的樂(lè)觀鎖插件,該插件會(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();
            // 樂(lè)觀鎖插件
            interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
            return interceptor;
        }
     
    }

    測(cè)試樂(lè)觀鎖代碼,我們創(chuàng)建兩個(gè)線(xiàn)程 A 和 B 分別去修改用戶(hù)ID為 1 的用戶(hù)年齡,然后觀察年齡和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_idnamesexageversion
    1測(cè)試00

    運(yùn)行上面程序,數(shù)據(jù)庫(kù)記錄如下:

    user_idnamesexageversion
    1測(cè)試016

    1.上面代碼將執(zhí)行10次循環(huán)操作,每次操作啟動(dòng)兩個(gè)線(xiàn)程(線(xiàn)程 A 和 線(xiàn)程 B)去修改用戶(hù)數(shù)據(jù)。

    2.如果數(shù)據(jù)沒(méi)有任何沖突,則用戶(hù)的年齡應(yīng)該是 20。但是上面程序運(yùn)行完成后年齡為 16

    3.這就說(shuō)明,在線(xiàn)程運(yùn)行的時(shí)候,可能A 剛好修改了version,并沒(méi)有執(zhí)行完,就到B線(xiàn)程了,就導(dǎo)致B線(xiàn)程修改失敗

    感謝各位的閱讀,以上就是“MybatisPlus中@Version注解如何使用”的內(nèi)容了,經(jīng)過(guò)本文的學(xué)習(xí)后,相信大家對(duì)MybatisPlus中@Version注解如何使用這一問(wèn)題有了更深刻的體會(huì),具體使用情況還需要大家實(shí)踐驗(yàn)證。這里是億速云,小編將為大家推送更多相關(guān)知識(shí)點(diǎn)的文章,歡迎關(guān)注!

    向AI問(wèn)一下細(xì)節(jié)

    免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

    AI