若依系统分页工具学习-PageHelper篇五

1,264 阅读2分钟

这是我参与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的主要工作包括:

  1. 处理缓存;
  2. 获取数据库连接;
  3. 创建Statement
  4. 执行SQL语句
  5. 处理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 方法在内部进行调用的

但是,往后看PageHelperPageInterceptor的代码:

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();

可见,作者后续也是实现了拦截Executor6个参数的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中的呢?那么到底分页时代码是如何自动变更的呢?