这是我参与11月更文挑战的第16天,活动详情查看:2021最后一次更文挑战
在昨天的文章中,我们阐述了PageInterceptor
的代码的前5行。今天我们接着看PageHelper的拦截器PageInterceptor
。
下一行代码为:
Executor executor = (Executor) invocation.getTarget();
复制代码
其中的Executor
的完整路径为org.apache.ibatis.executor.Executor
。
Executor
是Mybatis中的执行器,是一个接口。MyBatis中它的继承结构关系可以如下表示:
Executor:
-BaseExecutor:
-SimpleExecutor
-ReuseExeCutor
-BatchExeCutor
-CachingExecutor
复制代码
Executor
的主要工作包括:
- 处理缓存;
- 获取数据库连接;
- 创建
Statement
- 执行SQL语句
- 处理SQL执行结果
并且从Executor
中,我们发现了Executor
的两个query
方法:
public abstract <E> java.util.List<E> query(org.apache.ibatis.mapping.MappedStatement arg0, java.lang.Object arg1, org.apache.ibatis.session.RowBounds arg2, org.apache.ibatis.session.ResultHandler arg3, org.apache.ibatis.cache.CacheKey arg4, org.apache.ibatis.mapping.BoundSql arg5) throws java.sql.SQLException;
public abstract <E> java.util.List<E> query(org.apache.ibatis.mapping.MappedStatement arg0, java.lang.Object arg1, org.apache.ibatis.session.RowBounds arg2, org.apache.ibatis.session.ResultHandler arg3) throws java.sql.SQLException;
复制代码
根据PageHelper
中阐述的:
参数多的这个 query 方法都是被少的这个 query 方法在内部进行调用的
但是,往后看PageHelper
中PageInterceptor
的代码:
Executor executor = (Executor) invocation.getTarget();
CacheKey cacheKey;
BoundSql boundSql;
//由于逻辑关系,只会进入一次
if (args.length == 4) {
//4 个参数时
boundSql = ms.getBoundSql(parameter);
cacheKey = executor.createCacheKey(ms, parameter, rowBounds, boundSql);
} else {
//6 个参数时
cacheKey = (CacheKey) args[4];
boundSql = (BoundSql) args[5];
}
复制代码
其中的args
是从哪里来的呢?
是我们在上一篇文章:若依系统分页工具学习-PageHelper篇四提到的拦截器方法的第一句:
Object[] args = invocation.getArgs();
复制代码
可见,作者后续也是实现了拦截Executor
6个参数的query
方法的。
而且,这也印证了拦截器实际拦截的就是Executor
的query方法,只是有时拦截的4个参数的,有时拦截的是6个参数的。
后续代码最重要的几句:
//对 boundSql 的拦截处理
if (dialect instanceof BoundSqlInterceptor.Chain) {
boundSql = ((BoundSqlInterceptor.Chain) dialect).doBoundSql(BoundSqlInterceptor.Type.ORIGINAL, boundSql, cacheKey);
}
List resultList;
//调用方法判断是否需要进行分页,如果不需要,直接返回结果
if (!dialect.skip(ms, parameter, rowBounds)) {
//判断是否需要进行 count 查询
if (dialect.beforeCount(ms, parameter, rowBounds)) {
//查询总数
Long count = count(executor, ms, parameter, rowBounds, null, boundSql);
//处理查询总数,返回 true 时继续分页查询,false 时直接返回
if (!dialect.afterCount(count, parameter, rowBounds)) {
//当查询总数为 0 时,直接返回空的结果
return dialect.afterPage(new ArrayList(), parameter, rowBounds);
}
}
resultList = ExecutorUtil.pageQuery(dialect, executor,
ms, parameter, rowBounds, resultHandler, boundSql, cacheKey);
} else {
//rowBounds用参数值,不使用分页插件处理时,仍然支持默认的内存分页
resultList = executor.query(ms, parameter, rowBounds, resultHandler, cacheKey, boundSql);
}
复制代码
经过逐层判断,需要分页时,执行的是:
resultList = ExecutorUtil.pageQuery(dialect, executor,
ms, parameter, rowBounds, resultHandler, boundSql, cacheKey);
复制代码
这行代码才是需要分页时返回结果的代码。打开ExecutorUtil
的代码我们不难发现,在其内部差不多又是执行了一次各种判断,最终执行了executor.query
,那么这个过程中,分页SQL是何时插入到原SQL中的呢?那么到底分页时代码是如何自动变更的呢?