主要适用场景

意图:

当要更新一条记录的时候,希望这条记录没有被别人更新

乐观锁实现方式:

  • 取出记录时,获取当前version

  • 更新时,带上这个version

  • 执行更新时, set version = newVersion where version = oldVersion

  • 如果version不对,就更新失败

乐观锁配置需要2步 记得两步

1.插件配置

spring xml:

<bean class="com.baomidou.mybatisplus.extension.plugins.OptimisticLockerInterceptor"/>
spring boot: @Bean
public OptimisticLockerInterceptor optimisticLockerInterceptor() {
return new OptimisticLockerInterceptor();
}

2.注解实体字段 @Version 必须要!

@Version
private Integer version;

特别说明:

  • 支持的数据类型只有:int,Integer,long,Long,Date,Timestamp,LocalDateTime

  • 整数类型下 newVersion = oldVersion + 1

  • newVersion 会回写到 entity

  • 仅支持 updateById(id)update(entity, wrapper) 方法

  • update(entity, wrapper) 方法下, wrapper 不能复用!!!

示例

示例Java代码(参考代码)

int id = 100;
int version = 2;

User u = new User();
u.setId(id);
u.setVersion(version);
u.setXXX(xxx);

if(userService.updateById(u)){
System.out.println("Update successfully");
}else{
System.out.println("Update failed due to modified by others");
}

示例SQL原理

update tbl_user set name = 'update',version = 3 where id = 100 and version = 2
  • pom.xml

    <dependencies>
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter</artifactId>
    </dependency>
    <dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.2.0</version>
    </dependency>
    <dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    </dependency>
    <!-- https://mvnrepository.com/artifact/p6spy/p6spy -->
    <dependency>
    <groupId>p6spy</groupId>
    <artifactId>p6spy</artifactId>
    <version>3.8.0</version>
    </dependency>
    <dependency>
    <groupId>com.h2database</groupId>
    <artifactId>h2</artifactId>
    <scope>runtime</scope>
    </dependency>

    <dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.49</version>
    <scope>test</scope>
    </dependency>
    <!-- for testing -->
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
    </dependency>
    </dependencies>
  • 配置类

    @Configuration
    public class MybatisPlusOptLockerConfig {

    @Bean
    public OptimisticLockerInterceptor optimisticLockerInterceptor() {
    return new OptimisticLockerInterceptor();
    }
    }
  • 实体类​

    @Data
    public class User {

    @TableId(value = "id", type = IdType.AUTO)
    private Long id;
    private String name;
    private Integer age;
    private String email;
    @Version
    private Integer version;
    }
  • Mapper

    public interface UserMapper extends BaseMapper<User> {

    }
  • 启动类

    @SpringBootApplication
    @MapperScan("com.mp.locker.mapper") //不加在容器里面获取不了
    public class LockerApplication {

    public static void main(String[] args) {
    SpringApplication.run(LockerApplication.class, args);
    }

    }
  • application.yml

    spring:
    datasource:
    driver-class-name: com.p6spy.engine.spy.P6SpyDriver
    url: jdbc:p6spy:h2:tcp://192.168.180.115:19200/~/mem/test
    username: root
    password: test
  • 测试类

    @SpringBootTest
    class LockerApplicationTests {

    @Autowired(required = false)
    UserMapper userMapper;

    @Test
    public void testUpdateByIdSucc() {
    User user = new User();
    user.setAge(18);
    user.setEmail("test@baomidou.com");
    user.setName("optlocker");
    user.setVersion(1);
    userMapper.insert(user);
    Long id = user.getId();
    //INSERT INTO user ( name, version, email, age ) VALUES ( 'optlocker', 1, 'test@baomidou.com', 18 )
    User userUpdate = new User();
    userUpdate.setId(id);
    userUpdate.setAge(19);
    userUpdate.setVersion(1);
    //UPDATE user SET version=2, age=19 WHERE id=6 AND version=1
    Assert.assertEquals("Should update success", 1, userMapper.updateById(userUpdate));
    //6 optlocker 19 test@baomidou.com 2 值变成2
    Assert.assertEquals("Should version = version+1", 2, userUpdate.getVersion().intValue());
    }

    @Test
    public void testUpdateByIdFail() {
    User user = new User();
    user.setAge(18);
    user.setEmail("test@baomidou.com");
    user.setName("optlocker");
    user.setVersion(1);
    //INSERT INTO user ( name, version, email, age ) VALUES ( 'optlocker', 1, 'test@baomidou.com', 18 )
    userMapper.insert(user);
    Long id = user.getId();

    User userUpdate = new User();
    userUpdate.setId(id);
    userUpdate.setAge(19);
    userUpdate.setVersion(0);
    //UPDATE user SET version=1, age=19 WHERE id=7 AND version=0
    userMapper.updateById(userUpdate);
    }

    @Test
    public void testUpdateByIdSuccWithNoVersion() {
    User user = new User();
    user.setAge(18);
    user.setEmail("test@baomidou.com");
    user.setName("optlocker");
    user.setVersion(1);
    userMapper.insert(user);
    Long id = user.getId();

    User userUpdate = new User();
    userUpdate.setId(id);
    userUpdate.setAge(19);
    userUpdate.setVersion(null);
    Assert.assertEquals("Should update success as no version passed in", 1, userMapper.updateById(userUpdate));
    User updated = userMapper.selectById(id);
    Assert.assertEquals("Version not changed", 1, updated.getVersion().intValue());
    Assert.assertEquals("Age updated", 19, updated.getAge().intValue());
    }

    /**
    * 批量更新带乐观锁
    * <p>
    * update(et,ew) et:必须带上version的值才会触发乐观锁
    */
    @Test
    public void testUpdateByEntitySucc() {
    QueryWrapper<User> ew = new QueryWrapper<>();
    ew.eq("version", 1);
    int count = userMapper.selectCount(ew);

    User entity = new User();
    entity.setAge(28);
    entity.setVersion(1);

    Assert.assertEquals("updated records should be same", count, userMapper.update(entity, null));
    ew = new QueryWrapper<>();
    ew.eq("version", 1);
    Assert.assertEquals("No records found with version=1", 0, userMapper.selectCount(ew).intValue());
    ew = new QueryWrapper<>();
    ew.eq("version", 2);
    Assert.assertEquals("All records with version=1 should be updated to version=2", count, userMapper.selectCount(ew).intValue());
    }

    }

SpringBoot整合MybatisPlus3.X之乐观锁(十三)的更多相关文章

  1. SpringBoot整合MyBatis-Plus3.1详细教程

    作者:Sans_ juejin.im/post/5cfa6e465188254ee433bc69 一.说明 Mybatis-Plus是一个Mybatis框架的增强插件,根据官方描述,MP只做增强不做改 ...

  2. SpringBoot整合MybatisPlus3.X之SQL执行分析插件(十四)

    pom.xml <dependencies> <dependency> <groupId>org.springframework.boot</groupId& ...

  3. SpringBoot整合MybatisPlus3.X之自定义Mapper(十)

    pom.xml <dependencies> <dependency> <groupId>org.springframework.boot</groupId& ...

  4. SpringBoot整合MybatisPlus3.X之SQL注入器(九)

    pom.xml <dependencies> <dependency> <groupId>org.springframework.boot</groupId& ...

  5. SpringBoot整合MybatisPlus3.X之Wrapper(五)

    官方文档说明: 以下出现的第一个入参boolean condition表示该条件是否加入最后生成的sql中 以下代码块内的多个方法均为从上往下补全个别boolean类型的入参,默认为true 以下出现 ...

  6. SpringBoot整合MybatisPlus3.X之分页插件(四)

    注:详细请看2.X博客中,3.X直接上代码. 建议装一个MybatisX插件,可以在Mapper和Xml来回切换 pom.xml <dependencies> <dependency ...

  7. SpringBoot整合MybatisPlus3.X之逻辑删除(三)

    pom.xml <dependencies> <dependency> <groupId>org.springframework.boot</groupId& ...

  8. SpringBoot整合MybatisPlus3.X之Sequence(二)

    数据库脚本 DELETE FROM user; ​ INSERT INTO user (id, name, age, email) VALUES (, , 'test1@baomidou.com'), ...

  9. SpringBoot整合Mybatisplus3.x之CRUD(一)

    pom.xml <dependencies> <dependency> <groupId>org.springframework.boot</groupId& ...

随机推荐

  1. Scrapy项目 - 实现腾讯网站社会招聘信息爬取的爬虫设计

    通过使Scrapy框架,进行数据挖掘和对web站点页面提取结构化数据,掌握如何使用Twisted异步网络框架来处理网络通讯的问题,可以加快我们的下载速度,也可深入接触各种中间件接口,灵活的完成各种需求 ...

  2. 为博客添加 Gitalk 评论插件

    背景 Disqus需要翻墙才能正常使用 畅言有广告 2种评论系统都很难统一管理 优化 使用Gitalk评论插件 , gitalk 使用 Github 帐号登录,界面干净整洁,支持 MarkDown语法 ...

  3. 通过实例快速掌握k8s(Kubernetes)核心概念

    容器技术是微服务技术的核心技术之一,并随着微服务的流行而迅速成为主流.Docker是容器技术的先驱和奠基者,它出现之后迅速占领市场,几乎成了容器的代名词.但它在开始的时候并没有很好地解决容器的集群问题 ...

  4. 杭州蓝松科技---短视频SDK介绍

    蓝松短视频的口号和 更新周期: 我们的口号是:  蓝松短视频  任意个性化. 我们是杭州蓝松科技,  专业做视频短视频SDK的技术团队. 我们提供 Android/IOS平台上的 短视频编辑SDK,  ...

  5. 编写shell脚本实现一键创建KVM虚拟机

    shell脚本一键创建虚拟机 代码如下: #!/bin/bashname=$1 #把位置变量$1重新定义为name(创建虚拟机的名字)path1=/var/lib/libvirt/images/ #i ...

  6. k阶斐波那契数列fibonacci第n项求值

    已知K阶斐波那契数列定义为:f0 = 0,  f1 = 0, … , fk-2 = 0, fk-1 = 1;fn = fn-1 + fn-2 + … + fn-k , n = k , k + 1, … ...

  7. Java基础学习(七) - 异常处理

    1.异常概念 异常指的是程序在执行过程中出现的非正常的情况,导致JVM的非正常停止.在Java中,异常是一个类,产生异常就是创建异常对象并抛出一个异常对象. 异常指的并不是语法错误,语法错误,编译不会 ...

  8. React antd如何实现<Upload>组件上传附件再次上传已清除附件缓存问题。

    最近在公司做React+antd的项目,遇到一个上传组件的问题,即上传附件成功后,文件展示处仍然还有之前上传附件的缓存信息,需要解决的问题是,要把上一次上传的附件缓存在上传成功或者取消后,可以进行清除 ...

  9. 基于 H5 WebGL 的 3D 室内定位及电子围栏

    前言 现代工业化的推进在极大加速现代化进程的同时也带来的相应的安全隐患,在传统的可视化监控领域,一般都是基于 Web SCADA 的前端技术来实现 2D 可视化监控,本系统采用 Hightopo 的 ...

  10. mac下idea中安装docker插件

    idea中安装docker插件: 点击Intellij IDEA->Preferences...->Plugins->Browse repositories...如下: 点击Inst ...