乐观锁

注意:本教程使用的数据库脚本、数据模型和环境信息请参考 “MyBatis Plus环境准备” 章节,点击下载示例源码

乐观锁是相对悲观锁而言的,乐观锁假设数据一般情况下不会造成冲突,所以在数据进行提交更新的时候,才会正式对数据的冲突与否进行检测。如果发现冲突了,则返回给用户错误的信息,让用户决定如何去做。乐观锁适用于读操作多的场景,这样可以提高程序的吞吐量。

在 MyBatis Plus 中,提供 OptimisticLockerInnerInterceptor 插件和 @Version 注解来实现乐观锁。乐观锁的实现方式大致流程如下:

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

  • 更新时,带上这个 version

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

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

示例代码

(1)在 user 表中添加 version 字段。SQL 如下:

ALTER TABLE `user`
ADD COLUMN `version`  int UNSIGNED NULL COMMENT '版本信息';

(2)定义 user 表的 JavaBean,代码如下:

package com.hxstrive.mybatis_plus.model;
import com.baomidou.mybatisplus.annotation.*;
 
@TableName(value = "user")
public class AnnotationUser5Bean {
   @TableId(value = "user_id", type = IdType.AUTO)
   private String userId;
    
   @TableField("name")
   private String name;
    
   @TableField("sex")
   private String sex;
    
   @TableField("age")
   private Integer age;
    
   @Version
   private int version;
   // 忽略 getter 和 setter 方法
}

(3)添加 MyBatis Plus 的乐观锁插件,该插件会自动帮我们将 version 加一操作。如下:

package com.hxstrive.mybatis_plus;
 
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
 
@Configuration
public class MybatisPlusConfig {
    @Bean
    public MybatisPlusInterceptor paginationInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        // 乐观锁插件
        interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
        return interceptor;
    }
}

(4)测试乐观锁代码,我们创建两个线程 A 和 B 分别去修改用户ID为 1 的用户年龄,然后观察年龄和version字段的值。代码如下:

package com.hxstrive.mybatis_plus.simple_mapper.annotation;

import com.hxstrive.mybatis_plus.mapper.AnnotationUser5Mapper;
import com.hxstrive.mybatis_plus.model.AnnotationUser5Bean;
import org.junit.jupiter.api.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
 
@RunWith(SpringRunner.class)
@SpringBootTest
class AnnotationDemo5 {
 
    @Autowired
    private AnnotationUser5Mapper userMapper;
 
    @Test
    void contextLoads() throws Exception {
        AnnotationUser5Bean userBean = userMapper.selectById(userId);
        if (null == userBean) {
            return;
        }
        AnnotationUser5Bean newBean = new AnnotationUser5Bean();
        newBean.setName(userBean.getName());
        newBean.setSex(userBean.getSex());
        newBean.setAge(userBean.getAge() + 1);
        newBean.setUserId(userBean.getUserId());
        newBean.setVersion(userBean.getVersion());
        int result = userMapper.updateById(newBean);
        System.out.println("result=" + result + " ==> " + userBean);
    }
 
}

运行上面代码,每次更新数据时,MyBatis Plus 将使用下面的 SQL 语句:

Preparing: UPDATE user SET name=?, sex=?, age=?, version=? WHERE user_id=? AND version=?
Parameters: 测试(String), 男(String), 2(Integer), 3(Integer), 1(Integer), 2(Integer)

说说我的看法
全部评论(
没有评论
关于
本网站专注于 Java、数据库(MySQL、Oracle)、Linux、软件架构及大数据等多领域技术知识分享。涵盖丰富的原创与精选技术文章,助力技术传播与交流。无论是技术新手渴望入门,还是资深开发者寻求进阶,这里都能为您提供深度见解与实用经验,让复杂编码变得轻松易懂,携手共赴技术提升新高度。如有侵权,请来信告知:hxstrive@outlook.com
公众号