Mybatis-Plus插件扩展
Mybatis-Plus插件扩展
一、简介
1.1 MybatisPlusInterceptor
MybatisPlusInterceptor是核心插件,目前代理了Executor#query
和 Executor#update
和 StatementHandler#prepare
方法
属性
private List<InnerInterceptor> interceptors = new ArrayList<>();
1.2 InnerInterceptor
目前提供您的插件都将基于此接口来实现功能。
目前已有的功能:
- 自动分页: PaginationInnerInterceptor
- 多租户: TenantLineInnerInterceptor
- 动态表名: DynamicTableNameInnerInterceptor
- 乐观锁: OptimisticLockerInnerInterceptor
- sql性能规范: IllegalSQLInnerInterceptor
- 防止全表更新与删除: BlockAttackInnerInterceptor
注意: 使用多个功能需要注意顺序关系,建议使用如下顺序
- 多租户,动态表名
- 分页,乐观锁
- sql性能规范,防止全表更新与删除
总结: 对sql进行单次改造的优先放入,不对sql进行改造的最后放入
二、插件扩展
2.1 分页插件
配置文件中:
这里本应该多加一个@MapperScan注解,但是我将该注解写在SpringBoot的启动类上面了,所以你看情况要不要加。
package com.baomidou.mybatisplus.config;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.BlockAttackInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
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 PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
}
实体类Employee.java
package com.baomidou.mybatisplus.beans;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.Version;
import com.baomidou.mybatisplus.extension.activerecord.Model;
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.Data;
import java.io.Serializable;
/**
* <p>
*
* </p>
*
* @author xiongtete
* @since 2021-06-22
*/
@Data
@TableName("tbl_employee")
public class Employee extends Model<Employee> {
private static final long serialVersionUID = 1L;
@TableId(value = "id", type = IdType.AUTO)
private Integer id;
private String lastName;
private String email;
private String gender;
private Integer age;
@Version
private Integer version;
}
数据库初始数据
测试分页插件:
@Test
public void testPage(){
Page<Employee> employeePage = employeeMapper.selectPage(new Page<Employee>(1, 2),
null);
List<Employee> records = employeePage.getRecords();
records.forEach(System.out::println);
System.out.println("========分页相关的信息=======");
System.out.println("总条数:"+employeePage.getTotal());
System.out.println("当前页码:"+employeePage.getCurrent());
System.out.println("总页码:"+employeePage.getPages());
System.out.println("每页显示的条数:"+employeePage.getSize());
System.out.println("是否有上一页:"+employeePage.hasPrevious());
System.out.println("是否有下一页:"+employeePage.hasNext());
}
上图的红色部分不用管,那个是我加入了性能分析插件输出的信息
2.2 SQL分析打印
该功能依赖 p6spy 组件,完美的输出打印 SQL 及执行时长 3.1.0 以上版本
2.2.1 引入依赖:
<dependency>
<groupId>p6spy</groupId>
<artifactId>p6spy</artifactId>
<version>3.9.1</version>
</dependency>
2.2.2 application.yml配置
# DataSource Config
spring:
datasource:
url: jdbc:p6spy:mysql://localhost:3306/mp?&useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true
username: root
password: 123456
driver-class-name: com.p6spy.engine.spy.P6SpyDriver
type: com.alibaba.druid.pool.DruidDataSource
# mybatis-plus配置控制台打印sql语句:
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
# driver-class-name 为 p6spy 提供的驱动类
# url 前缀为 jdbc:p6spy 跟着冒号为对应数据库连接地址
# 打印出sql为null,在excludecategories增加commit
# 批量操作不打印sql,去除excludecategories中的batch
# 批量操作打印重复的问题请使用MybatisPlusLogFactory (3.2.1新增)
# 该插件有性能损耗,不建议生产环境使用。
2.2.3 spy.properties配置
#3.2.1以上使用
modulelist=com.baomidou.mybatisplus.extension.p6spy.MybatisPlusLogFactory,com.p6spy.engine.outage.P6OutageFactory
#3.2.1以下使用或者不配置
#modulelist=com.p6spy.engine.logging.P6LogFactory,com.p6spy.engine.outage.P6OutageFactory
# 自定义日志打印
logMessageFormat=com.baomidou.mybatisplus.extension.p6spy.P6SpyLogger
#日志输出到控制台
appender=com.baomidou.mybatisplus.extension.p6spy.StdoutLogger
# 使用日志系统记录 sql
#appender=com.p6spy.engine.spy.appender.Slf4JLogger
# 设置 p6spy driver 代理
deregisterdrivers=true
# 取消JDBC URL前缀
useprefix=true
# 配置记录 Log 例外,可去掉的结果集有error,info,batch,debug,statement,commit,rollback,result,resultset.
excludecategories=info,debug,result,commit,resultset
# 日期格式
dateformat=yyyy-MM-dd HH:mm:ss
# 实际驱动可多个
#driverlist=org.h2.Driver
# 是否开启慢SQL记录
outagedetection=true
# 慢SQL记录标准 2 秒
outagedetectioninterval=2
然后测试
@Test
public void test1(){
Employee employee=new Employee();
employee.setLastName("王老师");
employee.setEmail("wang@qq.com");
employee.setGender("1");
employee.setAge(22);
int result = employeeMapper.insert(employee);
System.out.println(result);
}
2.3 防止全表更新与删除
配置文件:
//注册防止全表更新与删除插件
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor1(){
BlockAttackInnerInterceptor blockAttackInnerInterceptor=new BlockAttackInnerInterceptor();
MybatisPlusInterceptor interceptor=new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(blockAttackInnerInterceptor);
return interceptor;
}
然后我们测试个删除全表的方法
/**
* 测试sql执行分析插件
*/
@Test
public void testSQLExplain(){
Integer delete = employeeMapper.delete(null);//全表删除
}
由上图可看出,全表删除直接抛异常了,这种插件建议只在开发环境中使用,生产环境不要用了。
2.4 乐观锁OptimisticLockerInnerInterceptor
当要更新一条记录的时候,希望这条记录没有被别人更新
乐观锁实现方式:
- 取出记录时,获取当前version
- 更新时,带上这个version
- 执行更新时, set version = newVersion where version = oldVersion
- 如果version不对,就更新失败
2.4.1 配置插件
SpringBoot注解方式:
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
return interceptor;
}
在实体类的字段上加上@Version注解
@Version
private Integer version;
说明:
- 支持的数据类型只有:int,Integer,long,Long,Date,Timestamp,LocalDateTime
- 整数类型下
newVersion = oldVersion + 1
newVersion
会回写到 entity 中- 仅支持
updateById(id)
与update(entity, wrapper)
方法 - 在
update(entity, wrapper)
方法下,wrapper
不能复用!!!
2.4.2 测试
假设我们更新id为19的这条数据,现在的version为3,我们先将age改为33
@Test
public void testOptimisticLocker(){
//更新操作
Employee employee=new Employee();
employee.setId(19);
employee.setLastName("tom");
employee.setEmail("tom@sina.com");
employee.setGender("1");
employee.setAge(33);
employee.setVersion(3);
int i = employeeMapper.updateById(employee);
}
执行成功,version+1变为4
假如我们此时将代码中的version改为3再测试(也就是说两个人都想改同一条数据,但是其中一个人先该,另一个人后改,后改的这个人的version已经和数据库中的不一致了)
@Test
public void testOptimisticLocker(){
//更新操作
Employee employee=new Employee();
employee.setId(19);
employee.setLastName("tom");
employee.setEmail("tom@sina.com");
employee.setGender("1");
employee.setAge(33);
employee.setVersion(3);
int i = employeeMapper.updateById(employee);
}
通过控制台输出可以确定更新并没有执行成功,因为返回的受影响行数是0.
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?