MyBatisPlus笔记05 插件
coconutnut

https://www.bilibili.com/video/BV1rE41197jR

P49~54

mybatis的插件机制

MyBatis 允许在已映射语句执行过程中的某一点进行拦截

默认情况下,MyBatis 允许使用插件来拦截的方法调用包括:

  • Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)

  • ParameterHandler (getParameterObject, setParameters)

  • ResultSetHandler (handleResultSets, handleOutputParameters)

  • StatementHandler (prepare, parameterize, batch, update, query)

可以拦截:

  • 执行器的方法
  • 参数的处理
  • 结果集的处理
  • Sql语法构建的处理

自定义拦截器可以实现Interceptor接口,注入到Spring容器

执行分析插件

可用作阻断全表更新、删除的操作

注意:该插件仅适用于开发环境,不适用于生产环境(性能问题)

配置

MybatisPlusConfig中配置

1
2
3
4
5
6
7
8
@Bean
public SqlExplainInterceptor sqlExplainInterceptor(){
SqlExplainInterceptor sqlExplainInterceptor = new SqlExplainInterceptor();
List<ISqlParser> sqlParserList = new ArrayList<>();
sqlParserList.add(new BlockAttackSqlParser());
sqlExplainInterceptor.setSqlParserList(sqlParserList);
return sqlExplainInterceptor;
}

测试

1
2
3
4
5
6
7
8
9
10
@Autowired
private UserMapper userMapper;

@Test
public void testUpdateAll(){
User user = new User();
user.setAge(20);
int result = this.userMapper.update(user, null);
System.out.println("result = " + result);
}

结果:全表更新被阻断

1
Prohibition of table update operation

性能分析插件

用于输出每条 SQL 语句及其执行时间,可以设置最大执行时间,超过时间会抛出异常

配置

两种方式,可以像上面一样在Config中配置,也可以在xml中配置

这里用xml方式

mybatis-config.xml中

1
2
3
4
5
6
7
8
9
10
11
12
13
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<plugins>
<!-- SQL 执行性能分析,开发环境使用,线上不推荐。 maxTime 指的是 sql 最大执行时长 -->
<plugin interceptor="com.baomidou.mybatisplus.extension.plugins.PerformanceInterceptor">
<property name="maxTime" value="100" />
<!--SQL是否格式化 默认false-->
<property name="format" value="true" />
</plugin>
</plugins>
</configuration>

测试

执行testSelectById()

输出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Time:10 ms - ID:com.coconutnut.demo.mapper.UserMapper.selectById
Execute SQL:
SELECT
id,
user_name,
password,
name,
age,
email
FROM
tb_user
WHERE
id=2

User(id=2, userName=lisi, password=123456, name=李四, age=20, email=test2@itcast.cn)

乐观锁插件

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

实现方式:

  • 取出记录时,获取当前version
  • 更新时,带上这个version
  • 执行更新时, set version = newVersion where version = oldVersion
  • 如果version不对,就更新失败

配置

1
2
3
4
@Bean
public OptimisticLockerInterceptor optimisticLockerInterceptor() {
return new OptimisticLockerInterceptor();
}

Version字段

为表添加version字段,并且设置初始值为1

1
2
3
ALTER TABLE `tb_user`
ADD COLUMN `version` int(10) NULL AFTER `email`;
UPDATE `tb_user` SET `version`='1';

为User实体类添加version字段和注解

1
2
@Version
private Integer version;

测试

1
2
3
4
5
6
7
8
9
@Test
public void testUpdateVersion(){
User user = new User();
user.setAge(30);
user.setId(2L);
user.setVersion(1); // 当前version为1
int result = this.userMapper.updateById(user);
System.out.println("result = " + result);
}

输出

1
2
3
4
5
6
7
8
9
10
11
12
 Time:2 ms - ID:com.coconutnut.demo.mapper.UserMapper.updateById
Execute SQL:
UPDATE
tb_user
SET
age=30,
version=2
WHERE
id=2
AND version=1

result = 1

乐观锁插件添加了version条件,并设置新的version为2

如果再执行一次

1
2
3
4
5
6
7
8
9
10
11
12
 Time:1 ms - ID:com.coconutnut.demo.mapper.UserMapper.updateById
Execute SQL:
UPDATE
tb_user
SET
age=30,
version=2
WHERE
id=2
AND version=1

result = 0

此时version已经是2,不能执行了

正常的使用方式

1
2
3
4
5
6
7
8
9
10
11
12
@Test
public void testUpdateVersion(){
User user = new User();
user.setId(2L); // 查询条件

User userVersion = user.selectById(); // 当前版本
user.setAge(30); // 更新的数据
user.setVersion(userVersion.getVersion()); // 版本信息

int result = this.userMapper.updateById(user);
System.out.println("result = " + result);
}

特别说明

  • 支持的数据类型只有:int,Integer,long,Long,Date,Timestamp,LocalDateTime
  • 整数类型下 newVersion = oldVersion + 1
  • newVersion 会回写到 entity 中
  • 仅支持 updateById(id) 与 update(entity, wrapper) 方法
  • 在 update(entity, wrapper) 方法下, wrapper 不能复用