天天看點

mybatis-plus使用樂觀鎖插件

參考博文

https://baijiahao.baidu.com/s?id=1659469738216922362&wfr=spider&for=pc

1 沒有鎖的風險

開發不設鎖,就像沒穿褲衩。當海水退去,程式的漏洞就會被一覽無遺。現在沒出問題,隻是因為還有海水為你遮擋了老闆們的視線。

聽小編給你講個朋友的故事:一件商品,成本價是80元,售價是100元。老闆先是通知小李,說你去把商品價格增加50元。小李正在玩遊戲,耽擱了一個小時。正好一個小時後,老闆覺得商品價格增加到150元,價格太高,可能會影響銷量。又通知小王,你把商品價格降低30元。

此時,小李和小王同時操作商品背景系統。小李操作的時候,系統先取出商品價格100元;小王也在操作,取出的商品價格也是100元。小李将價格加了50元,并将100+50=150元存入了資料庫;小王将商品減了30元,并将100-30=70元存入了資料庫。是的,如果沒有鎖,小李的操作就完全被小王的覆寫了。

現在商品價格是70元,比成本價低10元。幾分鐘後,這個商品很快出售了1千多件商品,老闆虧1多萬。

2 樂觀鎖與悲觀鎖

上面的故事,如果是樂觀鎖,小王儲存價格前,會檢查下價格是否被人修改過了。如果被修改過了,則重新取出的被修改後的價格,150元,這樣他會将120元存入資料庫。如果是悲觀鎖,小李取出資料後,小王隻能等小李操作完之後,才能對價格進行操作,也會保證最終的價格是120元。

接下來小編将介紹如何在mybatis-plus項目中,添加樂觀鎖。

需求:是否能夠確定小李先操作的價格不會被小王後操作而覆寫,對應的版本号是否有自動增加。

注意

  • 樂觀鎖資料類型支援int、integer、long、timestamp
  • 僅支援updateById和update方法

1 搭建 springboot 整合 mybatis-plus環境,可參考博文

https://blog.csdn.net/qq_41712271/article/details/115756865

2 MybatisPlusPageConfig 配置類修改

package cn.huawei.guanggao.config;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MybatisPlusPageConfig {

        /*  舊版本配置
      @Bean
      public PaginationInterceptor paginationInterceptor(){
        return new PaginationInterceptor();
      }*/

    /**
     * 新的分頁插件 ,攔截器的原理感覺
     */
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));

        //樂觀鎖插件
        interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());

        return interceptor;
    }
}
           

3 建立一個商品類,字段為:id、name、price、version;這裡請注意,必須給version字段加上@Version的注解

CREATE TABLE goods
(
    id      INT AUTO_INCREMENT COMMENT '商品id'
        PRIMARY KEY,
    NAME    VARCHAR(30)   NULL COMMENT '商品名稱',
    stock   INT DEFAULT 0 NULL COMMENT '商品庫存',
    VERSION INT DEFAULT 0 NULL COMMENT '并發版本控制'
)
    COMMENT '商品表' CHARSET = utf8;
           
package cn.huawei.guanggao.pojo;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.annotation.Version;
import lombok.Data;
import java.io.Serializable;

@Data
@TableName("goods")
public class Goods implements Serializable {

    private static final long serialVersionUID = -1939317434177299884L;

    @TableId(value = "id", type = IdType.AUTO)
    private Integer id;
    private String name;

    private Integer stock;

    @Version
    private Integer version;
}
           

4 測試類

@Autowired
    private GoodsMapper goodsMapper;

    @Test
    void test_23() {
        //SELECT id,name,stock,version FROM goods WHERE id=2
        //id, name, stock, version
        //2, 水果, 100, 0
        Goods good1 = goodsMapper.selectById(2);

        //SELECT id,name,stock,version FROM goods WHERE id=2
        //id, name, stock, version
        //2, 水果, 100, 0
        Goods good2 = goodsMapper.selectById(2);

        //小李把把價格 +50
        good1.setStock(good1.getStock() + 50);
        //UPDATE goods SET name=水果, stock=150, version=1 WHERE id=2 AND version=0
        int i = goodsMapper.updateById(good1);
        System.out.println("###################"+i);
        //################### 1
        //以上修改成功

        //小王把價格 -30
        good2.setStock(good2.getStock() - 30);
        int result = goodsMapper.updateById(good2);
        //UPDATE goods SET name=水果, stock=70, version=1 WHERE id=2 AND version=0
        //此操作會會失敗,因為上面的加100操作成功後,version的版本已經是1
        System.out.println("-----------------"+result);
        //-----------------0

        //是以需要這樣來操作
        if (result == 0) {
            //SELECT id,name,stock,version FROM goods WHERE id=2
            //id, name, stock, version
            //2, 水果, 150, 1
            Goods p2 = goodsMapper.selectById(2);

            //UPDATE goods SET name=水果, stock=120, version=2 WHERE id=2 AND version=1
            p2.setStock(p2.getStock() - 30);
            goodsMapper.updateById(p2);
        }
        //最後的結果
        //SELECT id,name,stock,version FROM goods WHERE id=2
        //id, name, stock, version
        //2, 水果, 120, 2
        Goods goods3 = goodsMapper.selectById(2L);
        System.out.println("最後的結果:" + goods3.getStock());
    }
           

小李小王取出來的商品價格是100元,版本号是0,小李先操作,把價格改成了150元,同時,version自動加了1。這時小王操作就失敗了。然後小王重新取出新的商品,取出的商品價格是150元,版本号是1。小王正常操作,最終的價格是120元,版本是2,避免了風險。