Spring Boot 集成Mybatis-Plus(三)
前言
上一张讲解了mybatis-plus高级特性的条件构造器,通过条件构造器可以实现复杂的条件查询,本文将讲解Mybatis-plus的相关插件。
常用插件
分页插件
mybatis-plus提供了分页功能,需要开启分页插件才能生效。
@Bean
public MybatisPlusInterceptor paginationInterceptor()
{
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(paginationInnerInterceptor());
return interceptor;
}
/**
* 分页插件
* @return
*/
@Bean
public PaginationInnerInterceptor paginationInnerInterceptor()
{
PaginationInnerInterceptor paginationInnerInterceptor =new PaginationInnerInterceptor();
//设置数据库类型为mysql
paginationInnerInterceptor.setDbType(DbType.MYSQL);
return paginationInnerInterceptor;
}
注意:Mybatis-plus3.2以后的版本需要添加插件到MybatisPlusInterceptor中,否则会失效。
SQl性能检测插件
简介
主要针对不规范的SQL进行拦截,防止全表扫描,从而影响性能。
示例
插件配置
//添加插件
@Bean
public MybatisPlusInterceptor paginationInterceptor()
{
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
// 分页插件
interceptor.addInnerInterceptor(paginationInnerInterceptor());
//乐观锁
interceptor.addInnerInterceptor(optimisticLockerInterceptor());
//防全表更新与删除插件
interceptor.addInnerInterceptor(blockAttackInnerInterceptor());
//不规范SQL检测插件
interceptor.addInnerInterceptor(illegalSQLInnerInterceptor());
return interceptor;
}
/**
* Sql性能检测插件
* @return
*/
@Bean
public IllegalSQLInnerInterceptor illegalSQLInnerInterceptor()
{
return new IllegalSQLInnerInterceptor();
}
示例代码
@RequestMapping("/illegalSQLInnerInterceptor")
public String illegalSQLInnerInterceTest()
{
List<TUser> tUsers = userService.getUserList();
return "成功";
}
测试结果
### Cause: com.baomidou.mybatisplus.core.exceptions.MybatisPlusException: 非法SQL,必须要有where条件] with root cause
com.baomidou.mybatisplus.core.exceptions.MybatisPlusException: 非法SQL,必须要有where条件
可以看出针对不规范的SQL进行拦截。
忽略配置
如果需要对某个SQL语句进行忽略只需要配置illegalSql=1即可。
@InterceptorIgnore(illegalSql = "1")
List<TUser> getUserList();
总结
虽然此插件可以一定程度上规范SQL书写,也可以拓展自己的SQL规范逻辑,但是此插件还是需要根据实际的生产环境视情况进行配置。
全局拦截插件
简介
由于程序的bug可能导致把整个表都更新或者删除,在生产环境中这是十分危险的事情。Mybatis-plus的针对update、delete语句进行了拦截,防止恶意的全表更新和全表删除。
示例
插件配置
@Bean
public MybatisPlusInterceptor paginationInterceptor()
{
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
// 分页插件
interceptor.addInnerInterceptor(paginationInnerInterceptor());
//乐观锁
interceptor.addInnerInterceptor(optimisticLockerInterceptor());
//防全表更新与删除插件
interceptor.addInnerInterceptor(blockAttackInnerInterceptor());
return interceptor;
}
/**
* 防全表更新与删除插件
* @return
*/
@Bean
public BlockAttackInnerInterceptor blockAttackInnerInterceptor()
{
return new BlockAttackInnerInterceptor();
}
示例代码
@RequestMapping("/blockAttackInnerTest")
public String blockAttackInnerTest()
{
UpdateWrapper<TUser> updateWrapper =new UpdateWrapper<>();
//修改字段
updateWrapper.set("email", "52@1j234.com");
//全表更新
userService.update(null,updateWrapper);
return "成功";
}
测试结果
### Error updating database. Cause: com.baomidou.mybatisplus.core.exceptions.MybatisPlusException: Prohibition of table update operation
### The error may exist in com/skywares/mybatisplus/mapper/UserMapper.java (best guess)
执行全表更新或者全表删除时后端直接报错,提示信息为禁止表更新操作。
乐观锁插件
简介
乐观锁是对于数据冲突保持一种乐观态度,操作数据时不会对操作的数据进行加锁,只有到数据提交的时候才通过一种机制来验证数据是否存在冲突。
示例
插件配置
@Bean
public MybatisPlusInterceptor paginationInterceptor()
{
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
// 分页插件
interceptor.addInnerInterceptor(paginationInnerInterceptor());
//添加乐观锁
interceptor.addInnerInterceptor(optimisticLockerInterceptor());
return interceptor;
}
/**
* 乐观锁插件
* @return
*/
@Bean
public OptimisticLockerInnerInterceptor optimisticLockerInterceptor() {
return new OptimisticLockerInnerInterceptor();
}
实体中添加@Version
@Version
private int version;
示例代码
@RequestMapping("/optimisticLockerTest")
public String optimisticLockerTest()
{
//先查询version字段信息
TUser user= userService.getById(262010);
UpdateWrapper<TUser> updateWrapper =new UpdateWrapper<>();
//修改字段
updateWrapper.set("email", "52@1j234.com");
updateWrapper.eq("oid", 262010);
//在进行更新
userService.update(user,updateWrapper);
return "成功";
}
测试结果
==> Preparing: UPDATE t_user SET name=?, age=?, address=?, version=?, email=? WHERE deleteFlag=0 AND (oid = ? AND version = ?)
==> Parameters: 张三1(String), 1(Integer), 广东省:1(String), 3(Integer), 52@1j234.com(String), 262010(Integer), 2(Integer)
<== Updates: 1
总结
Mybatis-plus的乐观锁只能用于updateById(id) 与 update(entity, wrapper) 方法,且更新之前需要先查询verison信息,否则乐观锁会失效。
动态表名插件
简介
用户数据量大的表,通过日期进行了水平分表,需要通过日期参数,动态的查询数据,Mybatis-plus可以通过此插件解析替换设定表名为处理器的返回表名。
插件配置
@Bean
public MybatisPlusInterceptor paginationInterceptor()
{
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
//动态表名
interceptor.addInnerInterceptor(dynamicTableNameInnerInterceptor());
}
@Bean
public DynamicTableNameInnerInterceptor dynamicTableNameInnerInterceptor()
{
DynamicTableNameInnerInterceptor dynamicTableNameInnerInterceptor =new DynamicTableNameInnerInterceptor();
//
TableNameHandler tableNameHandler=new MyTableNameHandler();
dynamicTableNameInnerInterceptor.setTableNameHandler(tableNameHandler);
return dynamicTableNameInnerInterceptor;
}
动态表名规则处理类
public class MyTableNameHandler implements TableNameHandler
{
private Logger logger = LoggerFactory.getLogger(MyTableNameHandler.class);
@Override
public String dynamicTableName(String sql, String tableName)
{
logger.info("dynamicTableName sql:{},tableName:{}",sql,tableName);
//如果参数为空,则以不要动态动态查询表名
Map<String, Object> paramMap = RequestDataHelper.getRequestData();
if(paramMap==null || paramMap.isEmpty())
{
logger.info("dynamicTableName paramMap is null");
return tableName;
}
// 获取参数方法
paramMap.forEach((k, v) -> logger.info(k + "----" + v));
int random = (int) paramMap.get("tableNo");
String tableNo = "_1";
if (random % 2 == 1)
{
tableNo = "_2";
}
String queryTableName=tableName + tableNo;
logger.info("---------> queryTableName:{}",queryTableName);
return queryTableName;
}
}
参数存储
public class RequestDataHelper
{
/**
* 请求参数存取
*/
private static final ThreadLocal<Map<String, Object>> REQUEST_DATA = new ThreadLocal<>();
/**
* 设置请求参数
*
* @param requestData 请求参数 MAP 对象
*/
public static void setRequestData(Map<String, Object> requestData) {
REQUEST_DATA.set(requestData);
}
/**
* 获取请求参数
*
* @return 请求参数 MAP 对象
*/
public static Map<String, Object> getRequestData()
{
return REQUEST_DATA.get();
}
}
示例
@RequestMapping("/dynamicTableNameInner")
public String dynamicTableNameInner()
{
for (int i = 0; i < 3; i++)
{
Map<String, Object> tableNameMap=new HashMap<String, Object>();
int tableNo = new Random().nextInt(10);
tableNameMap.put("tableNo", tableNo);
RequestDataHelper.setRequestData(tableNameMap);
TUser user = userService.getById(1);
System.err.println(user.getName());
}
return "成功";
}
动态传递参数,通过规则获取表名,如果参数为空,则不需要动态获取表名。
测试结果
[http-nio-9090-exec-1] INFO [] c.s.m.config.MyTableNameHandler - ---------> queryTableName:t_user_1
JDBC Connection [com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@33c47a4b] will not be managed by Spring
==> Preparing: SELECT oid AS id,name,age,address,deleteFlag,version FROM t_user_1 WHERE oid=? AND deleteFlag=0
==> Parameters: 1(Integer)
<== Columns: id, name, age, address, deleteFlag, version
<== Row: 1, user_1, 28, null, 0, 0
<== Total: 1
Closing non transactional SqlSession
Creating a new SqlSession
SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@7f105f02] was not registered for synchronization because synchronization is not active
[http-nio-9090-exec-1] INFO [] c.s.m.config.MyTableNameHandler - dynamicTableName sql:SELECT oid AS id,name,age,address,deleteFlag,version FROM t_user WHERE oid=? AND deleteFlag=0,tableName:t_user
[http-nio-9090-exec-1] INFO [] c.s.m.config.MyTableNameHandler - tableNo----1
[http-nio-9090-exec-1] INFO [] c.s.m.config.MyTableNameHandler - ---------> queryTableName:t_user_2
JDBC Connection [com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@33c47a4b] will not be managed by Spring
==> Preparing: SELECT oid AS id,name,age,address,deleteFlag,version FROM t_user_2 WHERE oid=? AND deleteFlag=0
==> Parameters: 1(Integer)
<== Columns: id, name, age, address, deleteFlag, version
<== Row: 1, user_2, 28, null, 0, 0
<== Total: 1
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@7f105f02]
通过执行结果可以看出,执行SQL语句动态的获取表名t_user_1、t_user_2查询对应的数据。
总结
本文讲解了Mybatis-plus的相关插件,需要注意插件的加载顺序,动态表名、分页,乐观锁、Sql 性能规范,虽然提供了很多插件,但是还是根据项目的实际情况来选择使用相关插件,对于多租户插后面会专门讲解,Sql性能规范建议大家少用。
转载自:https://juejin.cn/post/7090837136076718110