MyBatis插件的核心功能在于拦截和修改MyBatis框架在执行过程中的行为。具体来说,它可以拦截以下四大核心组件的方法调用:
- Executor:执行器,负责SQL语句的执行和事务管理;
- StatementHandler:语句处理器,处理具体的SQL语句,包括预编译和参数设置等;
- ParameterHandler:参数处理器,负责将用户传递的参数转换成JDBC可识别的参数;
- ResultSetHandler:结果集处理器,负责将JDBC返回的结果集转换成用户所需的对象或集合。
通过拦截这些方法调用,MyBatis插件可以实现诸如SQL重写、日志记录、性能监控、事务管理增强等多种功能。
mybatis的插件如何使用,我们通过插件实现统计SQL的耗时功能。
1. 首先实现Mybatis的Interceptor接口
package com.lzj.plugins;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.MappedStatement;
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.apache.ibatis.session.RowBounds;
import java.util.Properties;
@Intercepts(value = {
@Signature(
type = Executor.class, //type:标记需要拦截的类
method = "query", //method: 标记是拦截类的那个方法
args = { //args:标记拦截方法的入参
MappedStatement.class,
Object.class,
RowBounds.class,
ResultHandler.class
}
)
}
)
public class LogPlugin implements Interceptor {
/*计算统计耗时的核心逻辑*/
@Override
public Object intercept(Invocation invocation) throws Throwable {
long startTime = System.currentTimeMillis();
Object result = invocation.proceed();
System.out.println("耗时:" + (System.currentTimeMillis() - startTime));
return result;
}
@Override
public Object plugin(Object target) {
return Interceptor.super.plugin(target);
}
/*设置插件的属性*/
@Override
public void setProperties(Properties properties) {
Interceptor.super.setProperties(properties);
}
}
2. 实现耗时拦截器后还需要在mybatis配置文件中配置拦截器
<plugins>
<plugin interceptor="com.lzj.plugins.LogPlugin"></plugin>
</plugins>
只需执行完上述2步后,就可以正确打印出SQL的耗时了。
执行案例结果如下所示
Opening JDBC Connection
Created connection 868815265.
==> Preparing: select * from car where name=?
==> Parameters: xiaoli(String)
<== Columns: name, brand
<== Row: xiaoli, BYD
<== Total: 1
耗时:154
另外Interceptor接口中还一个setProperties方法,那么该方法是起什么作用呢?其实该方法起到了向拦截器传递参数的作用。那么具体如何使用呢,还是以上面的例子,执行结果耗时打印出来是154,但是不知道是耗时154ms还是154s,比较模糊,假设这个单位我们支持通过参数传递。那么首先配置文件修改成如下所示,其中property指定了向拦截器传递一个name为MyPlugin,value为ms的参数
<plugins>
<plugin interceptor="com.lzj.plugins.LogPlugin" >
<property name="MyPlugin" value="ms"/>
</plugin>
</plugins>
然后自定义的LogPlugin拦截器类修改如下所示
package com.lzj.plugins;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.MappedStatement;
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.apache.ibatis.session.RowBounds;
import java.util.Properties;
@Intercepts(value = {
@Signature(
type = Executor.class,
method = "query",
args = {
MappedStatement.class,
Object.class,
RowBounds.class,
ResultHandler.class
}
)
}
)
public class LogPlugin implements Interceptor {
private Properties properties;
@Override
public Object intercept(Invocation invocation) throws Throwable {
long startTime = System.currentTimeMillis();
Object result = invocation.proceed();
System.out.println("耗时:" + (System.currentTimeMillis() - startTime) + properties.getProperty("MyPlugin"));
return result;
}
@Override
public Object plugin(Object target) {
return Interceptor.super.plugin(target);
}
@Override
public void setProperties(Properties properties) {
this.properties = properties;
}
}
再次执行结果打印的耗时就带单位了,如下所示
Opening JDBC Connection
Created connection 868815265.
==> Preparing: select * from car where name=?
==> Parameters: xiaoli(String)
<== Columns: name, brand
<== Row: xiaoli, BYD
<== Total: 1
耗时:146ms