天天看點

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的樂觀鎖插件就介紹到這裡。