mybatis 插件
PageHelper
先看 PageHelper 的插件签名
@Intercepts(
{
@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}),
@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class}),
}
)
- @Intercepts 表示这是一个 mybatis 的插件,可以配置多个 @Signature
- @Signature 包含三个属性 type、method、args
- type 表示要拦截的对象。4 个选项:Executor、ParameterHandler、ResultSetHandler 以及 StatementHandler
- method 表示要拦截的方法。对应拦截对象的方法
- args 表示拦截方法的参数。对应拦截对象的方法参数
PageHelper 的做法就是拦截 Executor 的 两个 query 方法
type 拦截对象
Executor
- update:该方法会在所有的 INSERT、 UPDATE、 DELETE 执行时被调用,如果想要拦截这些操作,可以通过该方法实现。
- query:该方法会在 SELECT 查询方法执行时被调用,方法参数携带了很多有用的信息,如果需要获取,可以通过该方法实现。
- queryCursor:当 SELECT 的返回类型是 Cursor 时,该方法会被调用。
- flushStatements:当 SqlSession 方法调用 flushStatements 方法或执行的接口方法中带有 @Flush 注解时该方法会被触发。
- commit:当 SqlSession 方法调用 commit 方法时该方法会被触发。
- rollback:当 SqlSession 方法调用 rollback 方法时该方法会被触发。
- getTransaction:当 SqlSession 方法获取数据库连接时该方法会被触发。
- close:该方法在懒加载获取新的 Executor 后会被触发。
- isClosed:该方法在懒加载执行查询前会被触发。
ParameterHandler
- getParameterObject:在执行存储过程处理出参的时候该方法会被触发。
- setParameters:设置 SQL 参数时该方法会被触发。
ResultSetHandler
- handleResultSets:该方法会在所有的查询方法中被触发(除去返回值类型为 Cursor 的查询方法),一般来说,如果我们想对查询结果进行二次处理,可以通过拦截该方法实现。
- handleCursorResultSets:当查询方法的返回值类型为 Cursor 时,该方法会被触发。
- handleOutputParameters:使用存储过程处理出参的时候该方法会被调用。
StatementHandler
- prepare:该方法在数据库执行前被触发。
- parameterize:该方法在 prepare 方法之后执行,用来处理参数信息。
- batch:如果 MyBatis 的全剧配置中配置了
defaultExecutorType="BATCH",执行数据操作时该方法会被调用。 - update:更新操作时该方法会被触发。
- query:该方法在 SELECT 方法执行时会被触发。
- queryCursor:该方法在 SELECT 方法执行时,并且返回值为 Cursor 时会被触发。
运行原理
没有插件的运行图
有插件的运行图
自定义拦截器
自定义一个拦截Executor的query()方法的插件,用于打印SQL语句以及执行时间
@Intercepts({@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})})
public class QueryPlugin implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];
Object parameter = invocation.getArgs()[1];
BoundSql boundSql = mappedStatement.getBoundSql(parameter);
String sql = boundSql.getSql();
long startTime = System.currentTimeMillis();
Object result = invocation.proceed();
long endTime = System.currentTimeMillis();
System.out.println("Executed SQL [" + sql + "] in " + (endTime - startTime) + "ms");
return result;
}
@Override
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
@Override
public void setProperties(Properties properties) {
}
}
拦截器注册
xml 方式
<plugins>
<plugin interceptor="com.xiaolyuh.mybatis.EditPlugin">
<property name="args1" value="参数示例"/>
</plugin>
</plugins>
springboot 方式
// 注册插件方式1
@Configuration
public class QueryPluginConfig {
@Bean
public QueryPlugin queryPlugin() {
return new QueryPlugin();
}
}
// 注册插件方式2
@Configuration
public class QueryPluginConfig {
// @Bean
// public ConfigurationCustomizer configurationCustomizer() {
// return new ConfigurationCustomizer() {
// @Override
// public void customize(org.apache.ibatis.session.Configuration configuration) {
// //插件拦截链采用了责任链模式,执行顺序和加入连接链的顺序有关
// QueryPlugin queryPlugin = new QueryPlugin();
// configuration.addInterceptor(queryPlugin);
// }
// };
// }
@Bean
public ConfigurationCustomizer configurationCustomizer() {
return configuration -> {
// 插件拦截链采用了责任链模式,执行顺序和加入连接链的顺序有关
QueryPlugin queryPlugin = new QueryPlugin();
configuration.addInterceptor(queryPlugin);
};
}
}