【mybatis-plus】什么是乐观锁?如何实现“乐观锁”

简介: 【mybatis-plus】什么是乐观锁?如何实现“乐观锁”

“乐观锁”这个词以前我也没听过。上次在测试需求的时候,查询数据库发现有一个version字段,于是请教开发这个字干嘛使,

人家回复我:乐观锁,解决并发更新用的。当时大家都忙,咱也不敢多问。


今天就来折腾一下“乐观锁”。


一、什么是乐观锁


乐观锁其实用一句话来形容其作用就是:当要更新一条记录的时候,希望这条记录没有被别人更新,从而实现线程安全的数据更新。


结合下场景,记得那是一张库存表,有一个字段记录商品库存,涉及多个地方都有可能去更新它:


  1. 程序A 查询到了这条数据,得到库存是800,准备+200更新成1000,但是还没更新。
  2. 程序B 也查询到了这条数据,得到库存是800,准备-200更新成600,并且提交更新了。


那么,这时候A再提交更新之后,B就会发现明明是自己是800-200=600,怎么最后变成了1000?


这就是因为A的事务导致了B的数据更新丢失。


文字可能读起来比较晦涩,有请灵魂画手:


1268169-20201225175616065-464765216.png


正常情况下:


  • 按先后顺序是, A先更新成1000,然后B再拿1000-200,更新成800,这样B就没异议了。


  • 或者实在要2个同时更新,那也只能有一个成功,这样也没异议。


二、MP来实现乐观锁


乐观锁的实现,通过增加一个字段,比如version,来记录每次的更新。


查询数据的时候带出version的值,执行更新的时候,会再去比较version,如果不一致,就更新失败。


还是用之前的user表,增加了新的字段version


1.在实体类里增加对于的字段,并且加上自动填充(你也可以每次手动填充)


@Data
public class User {
    @TableId(type = IdType.ID_WORKER)
    private Long id;
    private String name;
    private Integer age;
    private String email;
    @TableField(fill = FieldFill.INSERT)        // 新增的时候填充数据
    private Date createTime;
    @TableField(fill = FieldFill.INSERT_UPDATE) // 新增或修改的时候填充数据
    private Date updateTime;
    @TableField(fill = FieldFill.INSERT)
    @Version
    private Integer version; // 版本号
}
@Component //此注解表示 将其交给spring去管理
public class MyMetaObjectHandler implements MetaObjectHandler {
    @Override
    public void insertFill(MetaObject metaObject) {
        this.setFieldValByName("createTime", new Date(), metaObject);
        this.setFieldValByName("updateTime", new Date(), metaObject);
        this.setFieldValByName("version", 0, metaObject); //新增就设置版本值为0
    }
    @Override
    public void updateFill(MetaObject metaObject) {
        this.setFieldValByName("updateTime", new Date(), metaObject);
    }
}


2. 配置插件


为了便于管理,可以见一个包,用于存放各种配置类,顺便把配置在启动类里的mapper扫描也换到这里来。


package com.pingguo.mpdemo.config;
import com.baomidou.mybatisplus.extension.plugins.OptimisticLockerInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
// 配置扫描mapper的路径
@MapperScan("com.pingguo.mpdemo.mapper")
public class MpConfig {
    // 乐观锁插件
    @Bean
    public OptimisticLockerInterceptor optimisticLockerInterceptor() {
        return new OptimisticLockerInterceptor();
    }
}


3.测试乐观锁


先新增一条测试数据:


//    新增
    @Test
    void addUser() {
        User user = new User();
        user.setName("大周");
        user.setAge(22);
        user.setEmail("laowang@123.com");
        userMapper.insert(user);
    }


新增成功,可以看到version值是0。


1268169-20201226000957592-252682773.png


再来试一下正常的修改:


//      测试乐观锁
    @Test
    void testOptimisticLocker() {
        User user = userMapper.selectById(1342502561945915393L);
        user.setName("大周2");
        userMapper.updateById(user);
    }


修改成功,可以看到version 变成了1。


1268169-20201226001221904-1346182030.png


最后,模拟下并发更新,乐观锁更新失败的情况:


//  测试乐观锁-失败
    @Test
    void testOptimisticLockerFailed() {
        User user = userMapper.selectById(1342502561945915393L);
        user.setName("大周3");
        User user2 = userMapper.selectById(1342502561945915393L);
        user2.setName("大周4");
        userMapper.updateById(user2); // 这里user2插队到user前面,先去更新
        userMapper.updateById(user); // 这里由于user2先做了更新后,版本号不对,所以更新失败
    }


按照乐观锁的原理,user2是可以更新成功的,也就是name会修改为“大周4”,version会加1。user因为前后拿到的版本号不对,更新失败。


1268169-20201226002229077-438409579.png


结果符合预期,我们也可以看下mybatis的日志,进一步了解一下:


可以看到上面首先是2个查询,查询到的version都是1。


1268169-20201226003032053-482386364.png


接着,第一个执行update语句的时候,where条件中version=1,可以找到数据,于是更新成功,切更新version=2。


1268169-20201226171852673-509608099.png


ps:这里图丢了一个我重新补的一个数据,说明下意思,忽略ID与上面的不一致。


而第二个再执行update的时候,where条件version=1,已经找不到了,因为version已经被上面的更新成了2,所以更新失败。

相关文章
|
5月前
|
数据库
MyBatisPlus-乐观锁概念及实现步骤
MyBatisPlus-乐观锁概念及实现步骤
47 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来实现。
11827 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
401 0
Mybatis-Plus学习(二):乐观锁插件
Mybatis-Plus学习(二):乐观锁插件
216 0
Mybatis-Plus学习(二):乐观锁插件
|
XML Java 数据格式
项目技术点-MybatisPlus 实现乐观锁(2) | 学习笔记
快速学习 项目技术点-MybatisPlus 实现乐观锁(2)
117 0