Mybatis插件会对四个接口中方法进行拦截,这些接口是
- ParameterHandler: 处理参数设置问题
- ResultHandler: 结果处理
- StatementHandler: 负责连接数据库,执行sql
-
Executor: 对上述过程的调度组织.
@Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class})})
public class PaginationInterceptor implements Interceptor {
……
}
public interface Interceptor {
Object intercept(Invocation invocation) throws Throwable;
Object plugin(Object target);
void setProperties(Properties properties);
}
拦截器类需要实现Interceptor接口,还需要有注解@Intercepts标识具体哪个类的那个方法需要被拦截。
分页查询需要在intercept(Invocation invocation)调用之前,算出要查询的页具体在哪几行数据,并且重写sql,然后再进行后面的流程。
在MyBatis实现了statementHandler的有四个类:
- RoutingStatementHandler,这是一个封装类,它不提供具体的实现,只是根据Executor的类型,创建不同的类型StatementHandler。
- SimpleStatementHandler,这个类对应于JDBC的Statement对象,用于没有预编译参数的SQL的运行。
- PreparedStatementHandler 这个用于预编译参数SQL的运行。
- CallableStatementHandler 它将实存储过程的调度。
那具体实现类是哪个呢?追踪源码不难发现是RoutingStatementHandler
// Configuration.java
public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) {
StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler);
statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
return statementHandler;
}
// RoutingStatementHandler.java
private final StatementHandler delegate;
public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) {
switch (ms.getStatementType()) {
case STATEMENT:
delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler);
break;
case PREPARED:
delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler);
break;
case CALLABLE:
delegate = new CallableStatementHandler(executor, ms, parameter, rowBounds, resultHandler);
break;
default:
throw new ExecutorException("Unknown statement type: " + ms.getStatementType());
}
}
可以到看到,RoutingStatementHandler只是一个Wrapper,里面的delegate才是最终的实现类。那么具体是哪一个呢?
我们知道,任何框架的初始化都是解析配置文件的过程,然后把元数据加载到内存中。内存中的MappedStatement 对应的就是xml文件中的statement,其默认类型是PreparedStatement
<insert id="save" statementType="STATEMENT"><!-- STATEMENT,PREPARED 或CALLABLE -->
我们还看到会调用interceptorChain.pluginAll,返回的是代理对象(JDK动态带来)。所以我们调用的是代理对象。
public class InterceptorChain {
private final List<Interceptor> interceptors = new ArrayList<Interceptor>();
public Object pluginAll(Object target) {
for (Interceptor interceptor : interceptors) {
target = interceptor.plugin(target);
}
return target;
}
public void addInterceptor(Interceptor interceptor) {
interceptors.add(interceptor);
}
}
在Spring创建org.mybatis.spring.SqlSessionFactoryBean的时候,调用addInterceptor(),这里使用的责任链模式。
最后在同时的代码中看到一个小技巧:如果一共有N条记录,每页有M条记录,那么页数为
(N-1)/M + 1,其实这有漏洞,当N=0的时候,不能成立。
参考资料: