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

这是我参与11月更文挑战的第15天,活动详情查看:2021最后一次更文挑战

在上一篇文章“若依系统分页工具学习-PageHelper篇三”中,我们通过下载并查看PageHelper-Spring-Boot-Starter源码,了解到PageHelper是通过实现MyBatis拦截器接口org.apache.ibatis.plugin.InterceptorPageInterceptor类从而实现对SQL重写的。

那么问题来了XDM, PageInterceptor又是具体如何重写SQL的呢?

我们知道拦截器一般是通过反射机制实现的。(本文暂不展开说)。

拦截器中最重要的方法一般命名为intercept,形式一般是下面代码的样子:

1
2
3
4
5
6
java复制代码@Override
public Object intercept(Invocation invocation) {
// 逻辑代码前
invocation.invoke(); // 执行所拦截的实际代码
// 逻辑代码后
}

这里的invocation.invoke()实际就是相当于我们去执行接口代码或者是执行SQL,如此我们便可以全局的对程序接口或者SQL做某些修改。

我们来看一下PageInterceptor的拦截器代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
java复制代码@Override
public Object intercept(Invocation invocation) throws Throwable {
try {
Object[] args = invocation.getArgs();
MappedStatement ms = (MappedStatement) args[0];
Object parameter = args[1];
RowBounds rowBounds = (RowBounds) args[2];
ResultHandler resultHandler = (ResultHandler) args[3];
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];
}
checkDialectExists();
//对 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);
}
return dialect.afterPage(resultList, parameter, rowBounds);
} finally {
if(dialect != null){
dialect.afterAll();
}
}
}

代码有点多,我们来逐行分析:

前4句,获取拦截中的参数:

1
2
3
4
5
java复制代码Object[] args = invocation.getArgs();
MappedStatement ms = (MappedStatement) args[0];
Object parameter = args[1];
RowBounds rowBounds = (RowBounds) args[2];
ResultHandler resultHandler = (ResultHandler) args[3];

类型依次为MappedStatementObjectRowBoundsResultHandler,为了更清楚,我们简单列出其结构:大致可以根据其参数了解这个参数大概是什么类型

  1. MappedStatement (resource, configuration, id, fetchSize, timeout, statementType, resultSetType, sqlSource, cache, parameterMap, resultMaps, …, sqlCommandType, keyGenerator,keyProperties, keyColumns, databaseId, statementLog, LanguageDriver, resultSets);
  2. Object, 不做叙述;
  3. RowBounds: offset, limit, (类中还有常量NO_ROW_OFFSET=0, NO_ROW_LIMIT = 2147483647);
  4. ResultHandler<T>:只有一个抽象方法:
1
java复制代码public abstract  void handleResult(org.apache.ibatis.session.ResultContext<? extends T> arg0);

又是从哪里拦截到的这几个参数呢?或者说,我们拦截的到底是个什么东东?

PageHelper的wiki文档给出了答案:org.apache.ibatis.executor.Executor.

wiki中说:

在 MyBatis 的拦截器的文档部分,我们知道 Executor 中的 query 方法可以被拦截

其query方法形式为:

1
2
3
4
5
java复制代码<E> List<E> query(
MappedStatement ms,
Object parameter,
RowBounds rowBounds,
ResultHandler resultHandler) throws SQLException;

[手动狗头]看,是不是对应上了4个参数!

话说谁发明的springboot这种接口与实现模式,源码看的类似了,一顿翻找还是漏掉不少源码。

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

0%