31. 如何在MyBatis中使用自定义拦截器?有哪些常见应用场景?
在 MyBatis 中,自定义拦截器是一个强大的功能,允许开发者在 SQL 语句执行的不同阶段进行拦截、修改或扩展操作。拦截器可以用于日志记录、性能监控、权限检查等多种场景。MyBatis 提供了 Interceptor
接口来实现自定义拦截器。
如何在 MyBatis 中使用自定义拦截器?
1. 实现 Interceptor 接口
要创建一个自定义拦截器,首先需要实现 MyBatis 的 Interceptor
接口。这个接口有一个 intercept
方法,负责处理拦截的逻辑。
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.Plugin;
import org.apache.ibatis.plugin.Signature;
import java.sql.Connection;
import java.util.Properties;
@Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})
})
public class MyCustomInterceptor implements Interceptor {
@Overridepublic Object intercept(Invocation invocation) throws Throwable {// 在这里添加拦截逻辑StatementHandler statementHandler = (StatementHandler) invocation.getTarget();String sql = statementHandler.getBoundSql().getSql();System.out.println("Executing SQL: " + sql);// 调用原方法return invocation.proceed();}
@Overridepublic Object plugin(Object target) {return Plugin.wrap(target, this);}
@Overridepublic void setProperties(Properties properties) {// 设置拦截器的属性}
}
2. 配置拦截器
实现拦截器之后,需要在 MyBatis 配置文件中注册这个拦截器。
XML 配置:
<configuration><plugins><plugin interceptor="com.example.MyCustomInterceptor"><!-- 可以在这里传递自定义属性 --><property name="property1" value="value1"/></plugin></plugins>
</configuration>
Java 配置(如果使用 Spring):
@Bean
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();sessionFactory.setDataSource(dataSource);
// 注册自定义拦截器MyCustomInterceptor interceptor = new MyCustomInterceptor();sessionFactory.setPlugins(new Interceptor[]{interceptor});
return sessionFactory.getObject();
}
3. 使用拦截器
在你配置了拦截器后,MyBatis 会在执行 SQL 语句的过程中自动调用拦截器。根据拦截器的配置,它会在不同的执行阶段进行干预,例如 SQL 语句准备阶段、执行阶段、结果处理阶段等。
常见应用场景
自定义拦截器在 MyBatis 中有广泛的应用场景,以下是几个常见的应用:
1. SQL 日志记录
可以使用拦截器记录每次执行的 SQL 语句以及执行时间。这对于性能调优和问题排查非常有帮助。
示例:
@Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})
})
public class SqlLoggerInterceptor implements Interceptor {
@Overridepublic Object intercept(Invocation invocation) throws Throwable {long startTime = System.currentTimeMillis();
StatementHandler statementHandler = (StatementHandler) invocation.getTarget();String sql = statementHandler.getBoundSql().getSql();
try {return invocation.proceed();} finally {long endTime = System.currentTimeMillis();System.out.println("SQL: " + sql + " executed in " + (endTime - startTime) + " ms");}}
@Overridepublic Object plugin(Object target) {return Plugin.wrap(target, this);}
@Overridepublic void setProperties(Properties properties) {}
}
2. 数据权限控制
在一些应用中,可能需要在查询或更新数据时,基于用户角色或权限对 SQL 进行过滤或修改。例如,限制某些用户只能查询自己负责的记录。
示例:
@Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})
})
public class DataPermissionInterceptor implements Interceptor {
@Overridepublic Object intercept(Invocation invocation) throws Throwable {StatementHandler statementHandler = (StatementHandler) invocation.getTarget();String sql = statementHandler.getBoundSql().getSql();
// 基于某些条件修改 SQL,比如添加 where 条件限制if (someCondition()) {sql = modifySqlForPermission(sql);Field sqlField = statementHandler.getBoundSql().getClass().getDeclaredField("sql");sqlField.setAccessible(true);sqlField.set(statementHandler.getBoundSql(), sql);}
return invocation.proceed();}
private boolean someCondition() {// 检查权限或角色return true;}
private String modifySqlForPermission(String sql) {// 修改 SQL 以加入权限限制return sql + " AND user_id = 123";}
@Overridepublic Object plugin(Object target) {return Plugin.wrap(target, this);}
@Overridepublic void setProperties(Properties properties) {}
}
3. 分页处理
在实现数据库分页功能时,可以通过拦截器自动在 SQL 语句中添加 LIMIT
和 OFFSET
子句,避免手动在每个查询中添加分页逻辑。
示例:
@Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})
})
public class PaginationInterceptor implements Interceptor {
@Overridepublic Object intercept(Invocation invocation) throws Throwable {StatementHandler statementHandler = (StatementHandler) invocation.getTarget();String sql = statementHandler.getBoundSql().getSql();
// 添加分页参数int offset = 0;int limit = 10;sql = sql + " LIMIT " + offset + "," + limit;
Field sqlField = statementHandler.getBoundSql().getClass().getDeclaredField("sql");sqlField.setAccessible(true);sqlField.set(statementHandler.getBoundSql(), sql);
return invocation.proceed();}
@Overridepublic Object plugin(Object target) {return Plugin.wrap(target, this);}
@Overridepublic void setProperties(Properties properties) {}
}
4. 性能监控
拦截器可以用于收集和记录 SQL 执行的时间、次数等信息,帮助进行性能分析和优化。
示例:
@Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})
})
public class PerformanceMonitoringInterceptor implements Interceptor {
@Overridepublic Object intercept(Invocation invocation) throws Throwable {long startTime = System.nanoTime();
Object result = invocation.proceed();
long endTime = System.nanoTime();System.out.println("SQL executed in " + (endTime - startTime) / 1_000_000 + " ms");
return result;}
@Overridepublic Object plugin(Object target) {return Plugin.wrap(target, this);}
@Overridepublic void setProperties(Properties properties) {}
}
总结
-
自定义拦截器:在 MyBatis 中,自定义拦截器通过实现
Interceptor
接口来定义拦截逻辑,并通过@Intercepts
注解来指定要拦截的目标方法。 -
常见应用场景:SQL 日志记录、数据权限控制、分页处理、性能监控等。
-
配置拦截器:可以在 MyBatis 配置文件中或通过 Java 代码配置自定义拦截器,以实现对 SQL 执行过程的干预。
通过自定义拦截器,可以在 MyBatis 执行 SQL 的各个阶段插入自定义逻辑,从而实现对数据库操作的灵活控制和扩展。