Mybatis拦截器
- 概述
- 应用场景
- 项目结构
- 实现分页查询
- 其它拦截器的使用
概述
Mybatis允许使用者在映射语句执行过程中的某一些指定的节点进行拦截调用,通过织入拦截器,在不同节点修改一些执行过程中的关键属性,从而影响SQL的生成、执行和返回结果。
默认情况下,Mybatis支持四种对象拦截:
- Executor:拦截执行器的方法;
- ParameterHandler:拦截参数的处理;
- ResultSetHandler:拦截结果集的处理;
- StatementHandler:拦截Sql语法构建的处理;
执行顺序:Executor => StatementHandler => ParameterHandler => ResultSetHandler
注:本文代码基于《Mybatis一级缓存&二级缓存》中的“一级缓存(Spring整合Mybatis)”的代码进行调整。
应用场景
- 分页查询;
- 数据脱敏;
- 数据过滤;
- 监控Sql语句执行耗时;
项目结构
实现分页查询
StatementInterceptor.java
package com.mybatis.interceptor;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.lang.reflect.Field;
import java.sql.Connection;
import java.util.Properties;
/**
* @author honey
* @date 2023-08-02 15:25:26
*/
@Slf4j
@Intercepts({
@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class}),
})
@Component
public class StatementInterceptor implements Interceptor {
@Value("${mybatis.page-helper.rule}")
private String rule;
@Override
public Object intercept(Invocation invocation) throws Throwable {
StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
MetaObject metaObject = SystemMetaObject.forObject(statementHandler);
MappedStatement mappedStatement = (MappedStatement) metaObject.getValue("delegate.mappedStatement");
SqlCommandType sqlCommandType = mappedStatement.getSqlCommandType();
// 判断sql语句的类型
switch (sqlCommandType) {
case SELECT:
extendLimit(statementHandler);
break;
case INSERT:
case UPDATE:
case DELETE:
case FLUSH:
default:
break;
}
log.info("【StatementInterceptor】方法拦截前执行");
Object result = invocation.proceed();
log.info("【StatementInterceptor】方法拦截后执行");
return result;
}
@Override
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
@Override
public void setProperties(Properties properties) {
}
private void extendLimit(StatementHandler statementHandler) throws NoSuchFieldException, IllegalAccessException {
// 获取原生sql语句
BoundSql boundSql = statementHandler.getBoundSql();
Class<? extends BoundSql> aClass = boundSql.getClass();
// 使用反射机制修改原生sql语句
Field sql = aClass.getDeclaredField("sql");
sql.setAccessible(true);
String oldSqlStr = boundSql.getSql();
log.info("原生sql语句:{}", oldSqlStr);
// 加上分页规则
sql.set(boundSql, oldSqlStr + " " + rule);
}
}
application.yml
mybatis:
page-helper:
rule: limit 2
其它拦截器的使用
ExecutorInterceptor.java
package com.mybatis.interceptor;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.springframework.stereotype.Component;
import java.util.Properties;
/**
* @author honey
* @date 2023-08-02 18:11:28
*/
@Slf4j
@Intercepts({
@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})
})
@Component
public class ExecutorInterceptor implements Interceptor {
Properties properties;
@Override
public Object intercept(Invocation invocation) throws Throwable {
log.info("【ExecutorInterceptor】方法拦截前执行");
Object result = invocation.proceed();
log.info("【ExecutorInterceptor】方法拦截后执行");
return result;
}
@Override
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
@Override
public void setProperties(Properties properties) {
this.properties = properties;
}
}
ParameterInterceptor.java
package com.mybatis.interceptor;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.executor.parameter.ParameterHandler;
import org.apache.ibatis.plugin.*;
import org.springframework.stereotype.Component;
import java.sql.PreparedStatement;
import java.util.Properties;
/**
* @author honey
* @date 2023-08-02 18:22:15
*/
@Slf4j
@Intercepts({
@Signature(type = ParameterHandler.class, method = "setParameters", args = {PreparedStatement.class})
})
@Component
public class ParameterInterceptor implements Interceptor {
Properties properties;
@Override
public Object intercept(Invocation invocation) throws Throwable {
log.info("【ParameterInterceptor】方法拦截前执行");
Object result = invocation.proceed();
log.info("【ParameterInterceptor】方法拦截后执行");
return result;
}
@Override
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
@Override
public void setProperties(Properties properties) {
this.properties = properties;
}
}
ResultSetInterceptor.java
package com.mybatis.interceptor;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.executor.resultset.ResultSetHandler;
import org.apache.ibatis.plugin.*;
import org.springframework.stereotype.Component;
import java.sql.Statement;
import java.util.Properties;
/**
* @author honey
* @date 2023-08-02 18:24:00
*/
@Slf4j
@Intercepts({
@Signature(type = ResultSetHandler.class, method = "handleResultSets", args = {Statement.class})
})
@Component
public class ResultSetInterceptor implements Interceptor {
Properties properties;
@Override
public Object intercept(Invocation invocation) throws Throwable {
log.info("【ResultSetInterceptor】方法拦截前执行");
Object result = invocation.proceed();
log.info("【ResultSetInterceptor】方法拦截后执行");
return result;
}
@Override
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
@Override
public void setProperties(Properties properties) {
this.properties = properties;
}
}