天天看点

MyBatis-Plus 之乐观锁插件

MyBatis-Plus 之乐观锁插件

适用场景

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

乐观锁实现方式:

  1. 取出记录时,获取当前​

    ​version​

    ​。
  2. 更新时,带上这个​

    ​version​

    ​。
  3. 执行更新时, ​

    ​set version = newVersion where version = oldVersion​

    ​。
  4. 如果​

    ​version​

    ​不对,就更新失败。

接下来,我们来演示MyBatis-Plus的乐观锁插件。

首先创建一个数据库表,如下图所示:

MyBatis-Plus 之乐观锁插件

然后创建一个Spring Boot项目。

​pom.xml​

​如下:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.kaven</groupId>
    <artifactId>mybatis-plus</artifactId>
    <version>1.0-SNAPSHOT</version>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.4.RELEASE</version>
        <relativePath/>
    </parent>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-webflux</artifactId>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.4.0</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.49</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>      

​application.yml​

​如下:

spring:
  application:
    name: mybatis-plus
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    username: root
    password: ITkaven@123
    url: jdbc:mysql://127.0.0.1:3306/test?characterEncoding=utf-8&useSSL=false

server:
  port: 8085

logging:
  level:
    root: warn
    com.kaven.mybatisplus.dao: trace
  pattern:
    console: '%p%m%n'      

实体类User:

package com.kaven.mybatisplus.entity;

import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;

@TableName("user")
@Data
public class User{

    @TableId
    private String id;

    @TableField(value = "username")
    private String username;

    @TableField(value = "password")
    private String password;

    @TableField(value = "age")
    private Integer age;

    @Version
    @TableField(value = "version")
    private Integer version;
}      

注解​

​@Version​

​不要忘记加上。

Mapper接口UserMapper:

package com.kaven.mybatisplus.dao;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.kaven.mybatisplus.entity.User;
import org.springframework.stereotype.Component;


@Component
public interface UserMapper extends BaseMapper<User> {}      

启动类:

package com.kaven.mybatisplus;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
@MapperScan(basePackages = "com.kaven.mybatisplus.dao")
public class AppRun {
    public static void main(String[] args) {
        SpringApplication.run(AppRun.class , args);
    }
}      

注解​

​@MapperScan(basePackages = "com.kaven.mybatisplus.dao")​

​一定要加上。

接下来是关键一步,我们需要将乐观锁插件加到MyBatis-Plus插件库中。

package com.kaven.mybatisplus.config;

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 mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
        return interceptor;
    }
}      

其实就是创建一个​

​OptimisticLockerInnerInterceptor​

​​实例通过​

​addInnerInterceptor()​

​​加到​

​MybatisPlusInterceptor​

​​中,再将​

​MybatisPlusInterceptor​

​​封装成​

​bean​

​。

先来看一看数据库的数据。

MyBatis-Plus 之乐观锁插件

我们来测试一下乐观锁插件。

package com.kaven.mybatisplus.dao;

import com.kaven.mybatisplus.entity.User;
import org.junit.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
public class LockerTest {

    @Autowired
    private UserMapper userMapper;

    @Test
    public void updateById(){
        User user = userMapper.selectById("1");
        int version = user.getVersion();

        user.setPassword("new password");
        user.setVersion(version);
        int rows = userMapper.updateById(user);
        System.out.println("影响行数: "+rows);
    }
}      

结果如下:

MyBatis-Plus 之乐观锁插件

很显然结果是正确的,先查询,再更新,更新时帮我们加了版本号的条件。

特别说明

  • 支持的数据类型只有:​

    ​int​

    ​​、​

    ​Integer​

    ​​、​

    ​long​

    ​​、​

    ​Long​

    ​​、​

    ​Date​

    ​​、​

    ​Timestamp​

    ​​、​

    ​LocalDateTime​

    ​。
  • 整数类型下 ​

    ​newVersion = oldVersion + 1​

    ​。
  • ​newVersion​

    ​​ 会回写到 ​

    ​entity​

    ​ 中。
  • 仅支持 ​

    ​updateById(entity)​

    ​​ 与 ​

    ​update(entity, wrapper)​

    ​ 方法。
  • 在 ​

    ​update(entity, wrapper)​

    ​ 方法下, wrapper 不能复用。

上面是官网的说明。

在 ​

​update(entity, wrapper)​

​ 方法下, wrapper 不能复用,我们来测试一下。

package com.kaven.mybatisplus.dao;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.kaven.mybatisplus.entity.User;
import org.junit.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
public class LockerTest {

    @Autowired
    private UserMapper userMapper;

    @Test
    public void update(){

        LambdaQueryWrapper<User> userLambdaQueryWrapper = Wrappers.lambdaQuery();
        userLambdaQueryWrapper.eq(User::getId , "1");

        User user1 = userMapper.selectById("1");
        int version1 = user1.getVersion();
        user1.setPassword("new password"+version1);
        user1.setVersion(version1);

        int rows1 = userMapper.update(user1 , userLambdaQueryWrapper);
        System.out.println("影响行数: "+rows1);

        System.out.println("-------------------------------------------");

        User user2 = userMapper.selectById("1");
        int version2 = user2.getVersion();
        user2.setPassword("new password"+version2);
        user2.setVersion(version2);

        int rows2 = userMapper.update(user2 , userLambdaQueryWrapper);
        System.out.println("影响行数: "+rows2);
    }
}      

结果如下:

MyBatis-Plus 之乐观锁插件

可以看到第一次更新是正常的,而第二次更新的​

​sql​

​​语句是有问题的,​

​version​

​​在​

​WHERE​

​条件中出现了两次,且值还不一样,所以这肯定是更新不到数据的。

UPDATE user SET username=?, password=?, age=?, version=? WHERE (id = ? AND version = ? AND version = ?)      

所以,要记住,在 ​

​update(entity, wrapper)​

​ 方法下, wrapper 不能复用。

MyBatis-Plus的乐观锁插件就介绍到这里。