天天看点

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,避免了风险。