MyBatisPlus实现乐观锁

简介: MyBatisPlus实现乐观锁

MyBatisPlus实现乐观锁

1.概念

在讲概念之前,我们先来分析一个问题:

业务并发现象带来的问题:秒杀

  1. 假如有100个商品或者票在出售,为了能保证每个商品或者票只能被一个人购买,如何保证不会出现超买或者重复卖
  2. 对于这一类问题,其实有很多的解决方案可以使用
  3. 第一个最先想到的就是锁,锁在一台服务器中是可以解决的,但是如果在多台服务器下锁就没有办法控制,比如12306有两台服务器在进行卖票,在两台服务器上都添加锁的话,那也有可能会导致在同一时刻有两个线程在进行卖票,还是会出现并发问题
  4. 我们接下来介绍的这种方式是针对于小型企业的解决方案,因为数据库本身的性能就是个瓶颈,如果对其并发量超过2000以上的就需要考虑其他的解决方案了。

乐观锁:在关系数据库管理系统里,乐观并发控制(又名“乐观锁”,Optimistic Concurrency Control,缩写“OCC”)是一种并发控制的方法。它假设多用户并发的事务在处理时不会彼此互相影响,各事务能够在不产生锁的情况下处理各自影响的那部分数据。在提交数据更新之前,每个事务会先检查在该事务读取数据后,有没有其他事务又修改了该数据。如果其他事务有更新的话,那么会放弃修改。

简单来说,乐观锁主要解决的问题是当要更新一条记录的时候,希望这条记录没有被别人更新,如果别人更新了那么我就不更新了。

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,所以第二个线程会修改失败
    • 反之同理
    • 不管谁先执行都会确保只能有一个线程更新数据,这就是MP提供的乐观锁的实现原理分析。

3.实现乐观锁的具体步骤

  1. 在数据库表上添加version列,我这里直接创建一个新表方便演示
CREATE TABLE goods (
    id BIGINT(20) PRIMARY KEY AUTO_INCREMENT,
    NAME VARCHAR(32) NOT NULL,
    prices INT(10) NOT NULL ,
    `version` INT(10) NOT NULL DEFAULT 1
);

INSERT INTO goods VALUES(NULL,'book',10,1);
INSERT INTO goods VALUES(NULL,'computer',3000,1);

表的内容如下:

image-20220704214221536

  1. 在模型类中添加对应的属性
@Data
public class Goods {
    /**
     * 实现id自增长
     */
    @TableId(type = IdType.AUTO)
    private Integer id;
    private String name;
    //标记此属性为version列对应的属性
    @Version
    private Integer version;
}
  1. 添加乐观锁的拦截器
@Configuration
public class MybatisPlusConfig {
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor(){
        //1 创建MybatisPlusInterceptor拦截器对象
        MybatisPlusInterceptor mpInterceptor=new MybatisPlusInterceptor();
        //2 添加乐观锁拦截器
        mpInterceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
        return mpInterceptor;
    }
}
  1. 在测试类中执行更新操作
@SpringBootTest
class SpringBootMypatisPlusApplicationTests {

    @Autowired
    private GoodsDao goodsDao;

    @Test
    void test()
    {
        Goods goods = new Goods();
        goods.setId(1);
        goods.setPrices(100);
        goodsDao.updateById(goods);
    }
}

在这里插入图片描述

通过结果我们可以发现,我们创建的这个对象并没有设置version数据,所以也就没有更新version字段

添加version数据

@SpringBootTest
class SpringBootMypatisPlusApplicationTests {

    @Autowired
    private GoodsDao goodsDao;

    @Test
    void test()
    {
        Goods goods = new Goods();
        goods.setId(1);
        goods.setVersion(1);
        goods.setPrices(100);
        goodsDao.updateById(goods);
    }
}

在这里插入图片描述

此时你可以发现,我们传递的是1,更新时会将version+1得到2,然后更新数据到数据库表里。

此时我们需要注意的是,我们设置的version数据就相当于上面讲的oldVersion,如果和数据库表中的不同,则sql更新失败

所以我们要想实现乐观锁,首先就是要得到表中要查找的那条数据的version,然后拿version当条件在将version加1更新回到数据库表中,所以我们在查询的时候,需要对其进行查询

@SpringBootTest
class SpringBootMypatisPlusApplicationTests {

    @Autowired
    private GoodsDao goodsDao;

    @Test
    void test()
    {
        //1.先通过要修改的数据id将当前数据查询出来
        //获得version字段值
        Goods goods = goodsDao.selectById(1);
        //2.修改数据
        goods.setPrices(200);
        goodsDao.updateById(goods);
    }
}

在这里插入图片描述

现在我们来模拟一遍秒杀的过程,看看多人修改同一商品的时候,是不是只能有一个人修改成功。

@SpringBootTest
class SpringBootMypatisPlusApplicationTests {

    @Autowired
    private GoodsDao goodsDao;

    @Test
    void test() {
        //1.先通过要修改的数据id将当前数据查询出来
        //获得version字段值
        Goods goods1 = goodsDao.selectById(1); // version = 3
        Goods goods2 = goodsDao.selectById(1); // version = 3
        //2.修改数据
        goods1.setPrices(1000);
        goodsDao.updateById(goods1);// 修改完后 version = 4
        goods2.setPrices(1000);
        goodsDao.updateById(goods2);// 此时 oldversion的值已经发生变化,不在等于3,所以修改失败
    }
}

在这里插入图片描述

在这里插入图片描述

至此,一个简单的乐观锁就实现了。

笔记参考B站黑马程序员: 黑马程序员2022最新SSM框架教程
相关文章
|
5月前
|
数据库
MyBatisPlus-乐观锁概念及实现步骤
MyBatisPlus-乐观锁概念及实现步骤
49 0
|
5月前
|
Java 关系型数据库 数据库连接
干翻Mybatis源码系列之第十二篇:基于Mybatis Plugins做一个乐观锁
干翻Mybatis源码系列之第十二篇:基于Mybatis Plugins做一个乐观锁
|
5月前
|
XML Java 数据库
mybatis-plus乐观锁
mybatis-plus乐观锁
41 0
|
10月前
|
安全 Java 数据库连接
mybatis plus整合乐观锁
mybatis plus整合乐观锁
|
10月前
|
数据库
MyBatisPlus的乐观锁和悲观锁
MyBatisPlus的乐观锁和悲观锁
106 0
|
11月前
|
Java
MyBatisPlus(七)乐观锁
使用乐观锁的意图是:当要更新一条记录的时候,希望这条记录没有被别人更新。那么需要在表中增加一个字段version来实现。
11829 0
|
11月前
|
数据库
springboot+mybatis实现乐观锁
springboot+mybatis实现乐观锁
119 0
|
11月前
|
Java 数据库连接 数据库
Mybatis-Plus 进阶开发-自定义乐观锁插件
有的时候我们需要对mybatis-plus的乐观锁插件进行自定义修改。那么其实mybatis 是已经有个默认的实现,我们只需要将其继承并扩展即可。 0. OptimisticLockerInnerInterceptor 介绍 当要更新一条记录的时候,希望这条记录没有被别人更新 乐观锁实现方式: 取出记录时,获取当前 version 更新时,带上这个 version 执行更新时, set version = newVersion where version = oldVersion 如果 version 不对,就更新失败 但是我们也可以根据自己的业务需求实现自定义的乐观锁插件。 Mybati
407 0
Mybatis-Plus学习(二):乐观锁插件
Mybatis-Plus学习(二):乐观锁插件
216 0
Mybatis-Plus学习(二):乐观锁插件
|
XML Java 数据格式
项目技术点-MybatisPlus 实现乐观锁(2) | 学习笔记
快速学习 项目技术点-MybatisPlus 实现乐观锁(2)
118 0