MyBatis-Plus 之乐观锁插件
适用场景
当要更新一条记录时,希望这条记录没有被别人更新过。
乐观锁实现方式:
- 取出记录时,获取当前
。version
- 更新时,带上这个
。version
- 执行更新时,
。set version = newVersion where version = oldVersion
- 如果
不对,就更新失败。version
接下来,我们来演示MyBatis-Plus的乐观锁插件。
首先创建一个数据库表,如下图所示:
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiI0gTMx81dsQWZ4lmZf1GLlpXazVmcvwFciV2dsQXYtJ3bm9CX9s2RkBnVHFmb1clWvB3MaVnRtp1XlBXe0xCMy81dvRWYoNHLwEzX5xCMx8FesU2cfdGLwMzX0xiRGZkRGZ0Xy9GbvNGLpZTY1EmMZVDUSFTU4VFRR9Fd4VGdsYTMfVmepNHLrJXYtJXZ0F2dvwVZnFWbp1zczV2YvJHctM3cv1Ce-cmbw5yMxcjM0cDZjJjY1QGZ3ImNzYzX5QTNxgTM5AzLcFTMyIDMy8CXn9Gbi9CXzV2Zh1WavwVbvNmLvR3YxUjLyM3Lc9CX6MHc0RHaiojIsJye.png)
然后创建一个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的乐观锁插件就介绍到这里。