MyBatis-Plus 之樂觀鎖插件
适用場景
當要更新一條記錄時,希望這條記錄沒有被别人更新過。
樂觀鎖實作方式:
- 取出記錄時,擷取目前
。version
- 更新時,帶上這個
。version
- 執行更新時,
。set version = newVersion where version = oldVersion
- 如果
不對,就更新失敗。version
接下來,我們來示範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
。
先來看一看資料庫的資料。
我們來測試一下樂觀鎖插件。
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);
}
}
結果如下:
很顯然結果是正确的,先查詢,再更新,更新時幫我們加了版本号的條件。
特别說明
- 支援的資料類型隻有:
、int
、Integer
、long
、Long
、Date
、Timestamp
。LocalDateTime
- 整數類型下
。newVersion = oldVersion + 1
-
會回寫到 newVersion
中。entity
- 僅支援
與 updateById(entity)
方法。update(entity, wrapper)
- 在
方法下, wrapper 不能複用。update(entity, 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);
}
}
結果如下:
可以看到第一次更新是正常的,而第二次更新的
sql
語句是有問題的,
version
在
WHERE
條件中出現了兩次,且值還不一樣,是以這肯定是更新不到資料的。
UPDATE user SET username=?, password=?, age=?, version=? WHERE (id = ? AND version = ? AND version = ?)
是以,要記住,在
update(entity, wrapper)
方法下, wrapper 不能複用。
MyBatis-Plus的樂觀鎖插件就介紹到這裡。