MyBatis 拦截器(Interceptor)是 MyBatis 提供的一种强大机制,允许开发者在 SQL 映射语句执行过程中的特定节点进行拦截和定制处理。通过实现 org.apache.ibatis.plugin.Interceptor
接口并注册到 MyBatis 的配置文件中,可以插入自定义的逻辑来改变或增强 MyBatis 的默认行为。
使用样例:统计(查询,更新,批量更新)SQL执行次数及用时
import com.zhangziwa.practisesvr.utils.log.LogContext;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.session.ResultHandler;
import org.springframework.stereotype.Component;
import java.sql.Statement;
@Intercepts({
@Signature(type = StatementHandler.class, method = "query", args = {Statement.class, ResultHandler.class}),
@Signature(type = StatementHandler.class, method = "update", args = {Statement.class}),
@Signature(type = StatementHandler.class, method = "batch", args = {Statement.class})})
@Component
public class SqlExecuteInterceptor implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
System.err.println("***SqlExecuteInterceptor.intercept***");
long startTime = System.currentTimeMillis();
try {
return invocation.proceed();
} finally {
long executionCost = System.currentTimeMillis() - startTime;
LogContext.incrementSqlCount();
LogContext.incrementSqlCost(executionCost);
}
}
}
使用样例:统计查询结果的数据量
import com.zhangziwa.practisesvr.utils.log.LogContext;
import org.apache.ibatis.executor.resultset.ResultSetHandler;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Signature;
import org.springframework.stereotype.Component;
import java.sql.Statement;
import java.util.Collection;
@Intercepts({@Signature(type = ResultSetHandler.class, method = "handleResultSets", args = {Statement.class})})
@Component
public class SqlReadRowInterceptor implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
Object proceed = invocation.proceed();
if (proceed instanceof Collection) {
LogContext.incrementSqlSearchedRowCount(((Collection<?>) proceed).size());
} else {
LogContext.incrementSqlSearchedRowCount(0);
}
return proceed;
}
}
@Signature
注解的type
属性用于指定要拦截的接口类型
,它可以是以下几种类型的类::
Executor (执行器): 拦截org.apache.ibatis.executor.Executor接口的方法,主要用于处理
SQL语句的执行过程
,包括查询、更新、插入和删除等操作。 例如,它可以用来控制事务的边界、记录日志或者进行额外的数据验证。
ParameterHandler (参数处理器): 拦截org.apache.ibatis.mapping.ParameterHandler接口的方法,负责设置
SQL预编译语句中的参数值
。 拦截此类方法可以对传入的参数做进一步的处理,如加密、解密、类型转换等。
StatementHandler (SQL处理器): 拦截org.apache.ibatis.mapping.StatementHandler接口的方法,用于处理
SQL语法构建
相关的工作。 如动态SQL的解析与拼接、添加额外的查询条件等。
ResultSetHandler (结果集处理器): 拦截org.apache.ibatis.resultset.ResultSetHandler接口的方法,用于处理
数据库返回的结果集
,将结果集中的数据映射到Java对象上。 在拦截这个阶段时,可以对结果进行后处理、自定义结果映射规则或增加缓存逻辑等。
拦截器的执行顺序是按照Executor
-> ParameterHandler
-> StatementHandler
-> ResultSetHandler
的顺序进行.
如果有多个相同类型的拦截器,则按照添加到SqlSessionFactory
中的逆序来执行。
@Signature
注解的method
用于指定需要拦截的具体方法名称
,它可以是以下几种类型的类:
# 对于Executor接口,可能的方法包括:
int update(MappedStatement var1, Object var2) throws SQLException;
<E> List<E> query(MappedStatement var1, Object var2, RowBounds var3, ResultHandler var4, CacheKey var5, BoundSql var6) throws SQLException;
<E> List<E> query(MappedStatement var1, Object var2, RowBounds var3, ResultHandler var4) throws SQLException;
<E> Cursor<E> queryCursor(MappedStatement var1, Object var2, RowBounds var3) throws SQLException;
List<BatchResult> flushStatements() throws SQLException;
void commit(boolean var1) throws SQLException;
void rollback(boolean var1) throws SQLException;
CacheKey createCacheKey(MappedStatement var1, Object var2, RowBounds var3, BoundSql var4);
boolean isCached(MappedStatement var1, CacheKey var2);
void clearLocalCache();
void deferLoad(MappedStatement var1, MetaObject var2, String var3, CacheKey var4, Class<?> var5);
Transaction getTransaction();
void close(boolean var1);
boolean isClosed();
void setExecutorWrapper(Executor var1);
# 对于ParameterHandler接口,可能的方法是:
Object getParameterObject();
void setParameters(PreparedStatement var1) throws SQLException;
# 对于StatementHandler接口,可能的方法包括:
Statement prepare(Connection var1, Integer var2) throws SQLException;
void parameterize(Statement var1) throws SQLException;
void batch(Statement var1) throws SQLException;
int update(Statement var1) throws SQLException;
<E> List<E> query(Statement var1, ResultHandler var2) throws SQLException;
<E> Cursor<E> queryCursor(Statement var1) throws SQLException;
BoundSql getBoundSql();
ParameterHandler getParameterHandler();
# 对于ResultSetHandler接口,可能的方法有:
<E> List<E> handleResultSets(Statement var1) throws SQLException;
<E> Cursor<E> handleCursorResultSets(Statement var1) throws SQLException;
void handleOutputParameters(CallableStatement var1) throws SQLException;