Java開發學習(四十九)----MyBatisPlus更新語句之樂觀鎖

2022-12-30 06:00:51

1、概念

在講解樂觀鎖之前,我們還是先來分析下問題:

業務並行現象帶來的問題:秒殺

  • 假如有100個商品或者票在出售,為了能保證每個商品或者票只能被一個人購買,如何保證不會出現超買或者重複賣

  • 對於這一類問題,其實有很多的解決方案可以使用

  • 第一個最先想到的就是鎖,鎖在一臺伺服器中是可以解決的,但是如果在多臺伺服器下鎖就沒有辦法控制,比如12306有兩臺伺服器在進行賣票,在兩臺伺服器上都新增鎖的話,那也有可能會導致在同一時刻有兩個執行緒在進行賣票,還是會出現並行問題

  • 我們接下來介紹的這種方式是針對於小型企業的解決方案,因為資料庫本身的效能就是個瓶頸,如果對其並行量超過2000以上的就需要考慮其他的解決方案了。

簡單來說,樂觀鎖主要解決的問題是當要更新一條記錄的時候,希望這條記錄沒有被別人更新。

2、實現思路

樂觀鎖的實現方式:

  • 資料庫表中新增version列,比如預設值給1

  • 第一個執行緒要修改資料之前,取出記錄時,獲取當前資料庫中的version=1

  • 第二個執行緒要修改資料之前,取出記錄時,獲取當前資料庫中的version=1

  • 第一個執行緒執行更新時,set version = newVersion where version = oldVersion

    • newVersion = version+1 [2]

    • oldVersion = version [1]

  • 第二個執行緒執行更新時,set version = newVersion where version = oldVersion

    • newVersion = version+1 [2]

    • oldVersion = version [1]

  • 假如這兩個執行緒都來更新資料,第一個和第二個執行緒都可能先執行

    • 假如第一個執行緒先執行更新,會把version改為2,

    • 第二個執行緒再更新的時候,set version = 2 where version = 1,此時資料庫表的資料version已經為2,所以第二個執行緒會修改失敗

    • 假如第二個執行緒先執行更新,會把version改為2,

    • 第一個執行緒再更新的時候,set version = 2 where version = 1,此時資料庫表的資料version已經為2,所以第一個執行緒會修改失敗

    • 不管誰先執行都會確保只能有一個執行緒更新資料,這就是MybatisPlus提供的樂觀鎖的實現原理分析。

上面所說的步驟具體該如何實現呢?

3、實現步驟

分析完步驟後,具體的實現步驟如下:

步驟1:資料庫表新增列

列名可以任意,比如使用version,給列設定預設值為1

步驟2:在模型類中新增對應的屬性

根據新增的欄位列名,在模型類中新增對應的屬性值

@Data
//@TableName("tbl_user") 可以不寫是因為設定了全域性設定
public class User {
    @TableId(type = IdType.ASSIGN_UUID)
    private String id;
    private String name;
    @TableField(value="pwd",select=false)
    private String password;
    private Integer age;
    private String tel;
    @TableField(exist=false)
    private Integer online;
    private Integer deleted;
    @Version
    private Integer version;
}
步驟3:新增樂觀鎖的攔截器
@Configuration
public class MpConfig {
    @Bean
    public MybatisPlusInterceptor mpInterceptor() {
        //1.定義MybatisPlus攔截器
        MybatisPlusInterceptor mpInterceptor = new MybatisPlusInterceptor();
        //2.新增樂觀鎖攔截器
        mpInterceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
        return mpInterceptor;
    }
}
步驟4:執行更新操作

新增version資料

@SpringBootTest
class Mybatisplus03DqlApplicationTests {
​
    @Autowired
    private UserDao userDao;
    
    @Test
    void testUpdate(){
        User user = new User();
        user.setId(3L);
        user.setName("Jock666");
        user.setVersion(1);
        userDao.updateById(user);
    }
}

你會發現,我們傳遞的是1,MybatisPlus會將1進行加1,然後,更新回到資料庫表中。

所以要想實現樂觀鎖,首先第一步應該是拿到表中的version,然後拿version當條件在將version加1更新回到資料庫表中,所以我們在查詢的時候,需要對其進行查詢

@SpringBootTest
class Mybatisplus03DqlApplicationTests {
​
    @Autowired
    private UserDao userDao;
    
    @Test
    void testUpdate(){
        //1.先通過要修改的資料id將當前資料查詢出來
        User user = userDao.selectById(3L);
        //2.將要修改的屬性逐一設定進去
        user.setName("Jock888");
        userDao.updateById(user);
    }
}

大概分析完樂觀鎖的實現步驟以後,我們來模擬一種加鎖的情況,看看能不能實現多個人修改同一個資料的時候,只能有一個人修改成功。

@SpringBootTest
class Mybatisplus03DqlApplicationTests {
​
    @Autowired
    private UserDao userDao;
    
    @Test
    void testUpdate(){
       //1.先通過要修改的資料id將當前資料查詢出來
        User user = userDao.selectById(3L);     //version=3
        User user2 = userDao.selectById(3L);    //version=3
        user2.setName("Jock aaa");
        userDao.updateById(user2);              //version=>4
        user.setName("Jock bbb");
        userDao.updateById(user);               //verion=3?條件還成立嗎?
    }
}

執行程式,分析結果:

樂觀鎖就已經實現完成了,如果對於上面的這些步驟記不住咋辦呢?

參考官方檔案來實現:https://baomidou.com/pages/0d93c0/