Mybatis-PageHelper 版本演进全解:从 5.0.0 到 6.1.1 的功能变更、参数溯源与升级指南
Mybatis-PageHelper 版本演进全解:从 5.0.0 到 6.1.1 的功能变更、参数溯源与升级指南
本文以仓库中的官方变更日志 wikis/en/Changelog.md 为核心,完整梳理 Mybatis-PageHelper(Mybatis 通用分页插件)自 5.0.0(2017-01-02)至 6.1.1(2025-06-20)各版本的重大功能变更、新增配置参数与兼容性说明,并结合当前仓库源码逐一溯源每个参数的实际实现位置。读完后,你可以准确判断应升级到哪个版本、如何切换 JSqlParser 实现、以及各关键参数(如 asyncCount、countSqlParser、boundSqlInterceptors、replaceSql)在源码中的落点与生效机制。
一、版本时间线总览
变更日志按版本倒序记录了 5.0.0 之后的所有演进(更早版本的日志以中文撰写,见 wikis/zh/Changelog.md)。各里程碑版本的核心主题如下:
| 版本 | 发布日期 | 核心主题 |
|---|---|---|
| 6.1.1 | 2025-06-20 | SunDB/虚谷数据库支持、Spring Boot 3.x(Jakarta)兼容、依赖升级、Maven Central 新门户发布 |
| 6.1.0 | 2023-12-16 | SQL 解析架构 SPI 化重构,JSqlParser 升级 4.7,移除 shade 内嵌 |
| 6.0.0 | 2023-11-05 | 仅支持 JDK 8+、异步 count、PageParam 类、countSqlParser 接口化 |
| 5.3.3 | 2023-06-03 | openGauss、Kingbase 8、CirroData 方言识别 |
| 5.3.2 | 2022-09-18 | PageProperties 接口、CountMsIdGen 接口、keepOrderBy 等参数 |
| 5.3.1 | 2022-06-14 | CVE-2022-28111 orderBy 注入漏洞修复 |
| 5.3.0 | 2011-07 | AutoDialect 接口、运行时 using() 指定方言、启动 Banner |
| 5.2.0 | 2020-07-26 | BoundSqlInterceptor 拦截器链、Oracle 两种分页语法 |
| 5.1.10 | 2019-06-05 | SQL Server with(nolock) 默认处理切换为 regex 正则 |
| 5.1.5 | 2018-09-02 | aggregateFunctions 聚合函数前缀集合,count 查询更智能 |
| 5.0.4 | 2017-08-01 | countSuffix 自定义 count 查询 |
| 5.0.0 | 2017-01-02 | 全新 PageInterceptor 拦截器 + Dialect 方言体系 |
二、6.1.1(2025-06-20):新数据库支持与 Spring Boot 3.x 兼容
2.1 功能变更
该版本是截至当前仓库的最新版本(pom.xml 中 <version>6.1.1</version> 与之对应),主要变更包括:
- 新增 SunDB 数据库分页支持、新增**虚谷数据库(Xugu)**适配器;
- 修复 Jakarta/Javax
ServletRequest兼容性问题,支持 Spring Boot 3.x(对 Jakarta EE 用户是推荐升级点); - 新增
PageInfo.of()重载方法,支持手动指定总记录数构建分页信息——当前源码中确实提供了三个静态工厂:of(List)、of(long total, List)与of(List, int navigatePages),见 PageInfo.java; - 依赖升级:MyBatis 从 3.5.10 升至 3.5.19、Guava 从 32.0.0-jre 升至 33.4.8-jre、Logback Classic 升至 1.2.13。这些版本与 pom.xml 中声明的依赖完全一致(MyBatis 与 Guava 均为
optional依赖,即仅当项目中自行引入时才生效); - 修复
countBoundSql复制时additionalParameter未正确拷贝的问题; - Maven 发布流程从旧版 OSSRH(oss.sonatype.org)迁移到新版 Maven Central Portal(central.sonatype.com)——对应 pom.xml 中
central-publishing-maven-plugin 0.4.0插件与https://central.sonatype.com/api/v1/publisher/upload的distributionManagement配置。
2.2 pagehelper-sqlparser 模块拆分
6.1.1 中 pagehelper-sqlparser 升级至 6.1.1,并新增了 sqlparser4.9 和 sqlparser5.1 两个模块,分别适配 JSqlParser 4.9/5.0/5.1/5.2/5.3 各小版本,进一步降低插件与宿主项目 JSqlParser 版本的耦合。这与 6.1.0 引入的 SPI 架构一脉相承(见下文第三节)。
2.3 兼容性说明与升级建议
- 该版本对前一版本保持向后兼容;
- 新数据库支持:SunDB、虚谷数据库,使用这两类数据库的用户可直接使用分页功能;
- 官方建议所有用户升级,尤其是使用 Spring Boot 3.x 或需要新数据库支持的用户;升级前请确认依赖版本兼容性。
三、6.1.0(2023-12-16):SQL 解析 SPI 架构重构
6.1.0 是插件解析层的一次架构级重构,核心是"把 JSqlParser 从内嵌实现变为外部可选依赖 + SPI 扩展点"。
3.1 重构要点
- PageHelper 将 jsqlparser 声明为直接依赖作为中间接口,允许通过 SPI 替换默认解析实现;
- JSqlParser 升级至 4.7,并基于其重新实现了 orderBy、分页与 count 查询的解析;
- 简化
pom.xml:移除 shade 内嵌 jsqlparser 的方式,改为通过外部依赖选择不同 jsqlparser 版本,支持自定义 SPI 扩展; - SQL 解析不再使用线程池,支持 SPI 扩展覆盖
SqlParser实现; - SQL Server 分页改为
SqlServerSqlParser接口,新增sqlServerSqlParser参数可覆盖默认实现; OrderByParser抽取为OrderBySqlParser接口,新增orderBySqlParser参数可覆盖默认实现,且原静态方法改为普通实例方法,为后续接口化做准备;- JDK 8+ 之后不再需要 JSqlParser 接口(早期用于 SQL Server 特殊配置),该接口被移除,文档中相应参数标记为已废弃;
- 固定 maven-compiler-plugin 版本以消除告警、提升构建稳定性。仓库根目录下的 jsqlparser4_7兼容性改动.patch 即记录了 4.7 适配过程中的兼容改动。
3.2 切换 JSqlParser 实现版本
为兼容 jsqlparser 4.5、4.7 及可能的未来版本,官方创建了 pagehelper-sqlparser 项目,当时提供 4.5 与 4.7 两种实现,当前版本默认使用 4.7 的实现代码。若要切换到 4.5 实现,需先排除 pagehelper 传递的 jsqlparser,再显式引入对应实现:
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>6.1.1</version>
<exclusions>
<exclusion>
<groupId>com.github.jsqlparser</groupId>
<artifactId>jsqlparser</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>sqlparser4.5</artifactId>
<version>6.1.1</version>
</dependency>
3.3 SPI 优先级与源码印证
变更日志明确说明:SPI 替换默认实现的优先级低于 sqlServerSqlParser、orderBySqlParser、countSqlParser 参数指定的实现;未指定任何具体实现时,若存在 SPI 实现则 SPI 生效。这一点从源码结构可以得到印证:
- 通用 SQL 解析入口 SqlParserUtil 在静态初始化块中用
ServiceLoader.load(SqlParser.class)查找 SPI 实现,找不到时回退到SqlParser.DEFAULT; - SqlParser.DEFAULT 的实现与变更日志给出的代码完全一致——通过
CCJSqlParserUtil.newParser+withSquareBracketQuotation(true)直接解析,绕开了 JSqlParser 内部临时创建的Executors.newSingleThreadExecutor()单线程池:
CCJSqlParser parser = CCJSqlParserUtil.newParser(statementReader);
parser.withSquareBracketQuotation(true);
return parser.Statement();
- 方言层 AbstractDialect 中,
countSqlParser、orderBySqlParser均通过ClassUtil.newInstance(properties.getProperty(...), 接口.class, properties, 默认实现::new)构建——即"参数指定 > 默认实现"的加载模式,SqlServerDialect中的sqlServerSqlParser同理(见 SqlServerDialect.java)。
3.4 解析超时控制:sqlparser-timeout
JSqlParser 使用线程池的初衷是防止解析超时。如果你的业务遇到过解析超时问题,可引入官方提供的超时依赖(通过 SPI 覆盖默认实现,超时设为 10 秒):
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>sqlparser-timeout</artifactId>
<version>6.1.1</version>
</dependency>
SPI 实现示例可参考 pagehelper-sqlsource 模块中的代码。
四、6.0.0(2023-11-05):JDK 8 基线与运行时能力增强
- JDK 8 适配:6.0 起不再支持 JDK 6/7,仍需要旧环境的可继续使用 5.x 版本;当前 pom.xml 中
maven.compiler.source/target均固定为 8; - 异步 count:新增全局配置
asyncCount(默认false),单次启用方式为PageHelper.startPage(1, 10).enableAsyncCount()。异步查询使用**独立连接(事务)**执行,因此当查询受新增、删除、修改操作影响(存在事务可见性问题)时不宜开启;对应源码 PageParams.java 中asyncCount = Boolean.parseBoolean(properties.getProperty("asyncCount")); - JSqlParser 默认开启
parser.withSquareBracketQuotation(true)以支持 SQL Server 的[]语法(该参数在 6.1.0 中移除,改为直接内置于SqlParser.DEFAULT); PageInfo新增数据对象转换方法<E> PageInfo<E> convert(Page.Function<T, E> function),便于把Page<User>转换后的列表直接包装为新类型的PageInfo;CountSqlParser改为接口,countSqlParser参数可替换为自定义实现——当前接口定义见 CountSqlParser.java,核心方法为getSmartCountSql(String sql, String countColumn);dialectAlias支持简化配置:可直接引用方言缩写而非全限定类名,例如dm=oracle;oracle=oracle9i;解析逻辑位于 PageAutoDialect.java;countColumn增加注入检测,堵住该参数被注入的风险;- 新增
PageParam类:一个不嵌入对象的轻量分页参数承载类(注意会改变原有参数传递方式,如需用可自行继承扩展),见 PageParam.java; - 全部异常信息改为英文;
- 开放
setLocalPage方法; - 修复 SQL Server 含 union 语句时 orderBy 处理错误;优化 total 逻辑解决"查询不分页时 order by 失效"的问题;
- 修改方言实例化逻辑,保证配置完成后再使用方言对象;
dialectAliasMap改为LinkedHashMap,支持按配置顺序匹配; - 修复星云数据库分页 bug。
五、5.x 系列:从方言识别到拦截器链的渐进式演进
5.1 5.3.3 / 5.3.2:方言识别扩展与配置体系成型
5.3.3(2023-06-03):支持从 JDBC URL 解析 openGauss、Kingbase 8 对应方言,新增 CirroData 支持,并修复 SQL 注入校验不正确的判定问题。当前仓库 dialect/helper 目录下可见对应的 GaussDBDialect.java、CirroDataDialect.java 等方言类。
5.3.2(2022-09-18)是该版本线中配置体系成型的一版:
- 新增
debug参数(默认false),开启后记录调用堆栈,用于排查不安全的分页调用——对应 PageInterceptor.java 中的debug = Boolean.parseBoolean(properties.getProperty("debug")); - 支持 count SQL 的 hint 语法解析;
- 新增
PageProperties接口:框架内部实例化的扩展类可实现该接口以获取分页插件全部配置,见 PageProperties.java; - 新增
CountMsIdGen接口:通过countMsIdGen参数配置自定义实现,用于生成 count 查询对应的 msId;默认实现仍是countSuffix,并对selectByExample这类命名自动映射到selectCountByExample。当前接口定义与默认实现见 CountMsIdGen.java:
CountMsIdGen DEFAULT = (ms, parameter, boundSql, countSuffix) -> ms.getId() + countSuffix;
- 新增
keepOrderBy与keepSubSelectOrderBy配置(是否保留主查询 / 子查询中的 order by),源码位于 PageParams.java; - 新增
sqlParser配置及JSqlParser接口,解决 JSqlParser 与 JDK 的兼容性问题、免去额外配置(该参数在 6.1.0 中已移除); - 测试日志框架改用 Logback,移除 log4j;
- 修复
dialectKey为空导致的 NPE。
上述参数的完整用法说明见官方使用文档 wikis/en/HowToUse.md。
5.2 5.3.1:安全修复
5.3.1(2022-06-14)解决 CVE-2022-28111 漏洞——限制 orderBy 参数以防止 SQL 注入。使用旧版本且将 orderBy 参数暴露给外部输入的用户,应优先升级到 5.3.1 或更高版本。同版本还新增 AS400 数据库支持、优化 Page 类泛型参数、规范 PostgreSQL 分页参数顺序。
5.3 5.3.0:AutoDialect 与运行时指定方言
5.3.0(2021-10-07)引入了数据库类型自动识别体系:
- 新增
AutoDialect接口自动获取数据库类型,可通过autoDialectClass配置为自定义实现。默认使用DataSourceNegotiationAutoDialect(优先根据连接池获取):对hikari、druid、tomcat-jdbc、c3p0、dbcp类型的连接池,直接从连接池配置读取 jdbcUrl;其他类型数据源仍按旧方式获取连接。希望完全使用旧方式时可配置autoDialectClass=old;连接池类型明确时建议配置具体值,如使用 Hikari 时autoDialectClass=hikari。当前仓库 dialect/auto 目录下有HikariAutoDialect、DruidAutoDialect、TomcatAutoDialect、C3P0AutoDialect、DbcpAutoDialect等各连接池实现及DataSourceNegotiationAutoDialect协调类; - 运行时动态指定方言实现:
PageHelper.startPage(1, 10).using("oracle");
PageHelper.startPage(2, 10).using("org.exmaple.CustomDialect");
PageInfo新增空实例常量PageInfo.EMPTY与内容判断方法hasContent()(当前源码见 PageInfo.java);- 启动 Banner:debug 级别下输出分页插件启动横幅,可用
-Dpagehelper.banner=false或环境变量PAGEHELPER_BANNER=false关闭:
DEBUG [main] -
,------. ,--. ,--. ,--.
| .--. ' ,--,--. ,---. ,---. | '--' | ,---. | | ,---. ,---. ,--.--.
| '--' | ' ,-. | | .-. | | .-. : | .--. | | .-. : | | | .-. | | .-. : | .--'
| | --' \ '-' | ' '-' ' \ --. | | | | \ --. | | | '-' ' \ --. | |
`--' `--`--' .`- / `----' `--' `--' `----' `--' | |-' `----' `--'
`---' `--' is intercepting.
Banner 的设计目的是:当配置了多个分页插件时会看到多次横幅输出,从而发现重复实例化问题,并可结合 PageInterceptor 构造函数的断点定位实例化位置;
- Count 查询优化:含有
exists时不对查询列做优化;列中存在带别名的函数或运算时不优化查询列,避免 order by / having 中使用的别名丢失; - 兼容部分数据(如 TDEngine)在 count 查询无结果时返回 null 的判定问题;
- 新增 Firebird 数据库支持(与 SqlServer2012 分页语法一致)、Impala 自动识别;JSqlParser 升级至 4.2。
5.4 5.2.0:BoundSqlInterceptor 拦截器链(重点特性详解)
5.2.0(2020-07-26)最大的变化是新增 BoundSqlInterceptor,可在分页处理的三个阶段拦截、修改或读取 SQL(BoundSql 对象)。该接口至今仍是插件的扩展核心,当前定义见 BoundSqlInterceptor.java:
/**
* BoundSql 处理器
*/
public interface BoundSqlInterceptor {
/**
* boundsql 处理
*
* @param type 类型
* @param boundSql 当前类型的 boundSql
* @param cacheKey 缓存 key
* @param chain 处理器链,通过 chain.doBoundSql 方法继续执行后续方法,也可以直接返回 boundSql 终止后续方法的执行
* @return 允许修改 boundSql 并返回修改后的
*/
BoundSql boundSql(Type type, BoundSql boundSql, CacheKey cacheKey, Chain chain);
enum Type {
/**
* 原始SQL,分页插件执行前,先执行这个类型
*/
ORIGINAL,
/**
* count SQL,第二个执行这里
*/
COUNT_SQL,
/**
* 分页 SQL,最后执行这里
*/
PAGE_SQL
}
/**
* 处理器链,可以控制是否继续执行
*/
interface Chain {
Chain DO_NOTHING = (type, boundSql, cacheKey) -> boundSql;
BoundSql doBoundSql(Type type, BoundSql boundSql, CacheKey cacheKey);
}
}
实现时不需要关心 Chain,只需实现 boundSql 方法。执行时序分三种情况:
- 无论当前 SQL 是否会被分页,
Type.ORIGINAL类型的拦截方法都会执行; - 调用分页方法时,继续执行
Type.COUNT_SQL拦截方法——仅当执行分页且执行 count 查询时触发; - 调用分页方法且 count > 0 时,执行
Type.PAGE_SQL拦截方法——仅当实际执行分页 SQL 时触发。
即:ORIGINAL 在 SQL 执行前触发,COUNT_SQL 在 count 执行前触发,PAGE_SQL 在分页 SQL 执行前触发(count=0 时不会执行分页,也就不会触发 PAGE_SQL)。
配置方式一:插件属性(可配置多个,英文逗号分隔)
<plugin interceptor="com.github.pagehelper.PageInterceptor">
<!-- 支持通过Mapper接口参数来传递分页参数 -->
<property name="helperDialect" value="mysql"/>
<property name="boundSqlInterceptors"
value="com.github.pagehelper.test.basic.provider.TestBoundSqlInterceptor,com.github.pagehelper.test.basic.provider.TestBoundSqlInterceptor"/>
</plugin>
(上例中同一个拦截器重复配置两次,用于演示参数值可以是多个——该拦截器会执行两次。)属性解析位于 PageBoundSqlInterceptors.java。
配置方式二:PageHelper.startPage 时临时指定。该方式的拦截器会放在链头最先执行,因此你可以控制是否继续执行后续拦截器,也可以在所有后续执行完成、返回前做最终处理:
PageHelper.startPage(1, 10).boundSqlInterceptor(new BoundSqlInterceptor() {
@Override
public BoundSql boundSql(Type type, BoundSql boundSql, CacheKey cacheKey, Chain chain) {
System.out.println("before: " + boundSql.getSql());
BoundSql doBoundSql = chain.doBoundSql(type, boundSql, cacheKey);
System.out.println("after: " + doBoundSql.getSql());
if (type == Type.ORIGINAL) {
Assert.assertTrue(doBoundSql.getSql().contains(TestBoundSqlInterceptor.COMMENT));
}
return doBoundSql;
}
});
仓库中的测试用例展示了"只读/修改原始 SQL"的完整写法——在 Type.ORIGINAL 阶段给 SQL 追加注释(不影响执行),通过 MetaObjectUtil 反射设置 sql 属性,可参考 TestBoundSqlInterceptor:
public class TestBoundSqlInterceptor implements BoundSqlInterceptor {
public static final String COMMENT = "\n /* TestBoundSqlInterceptor */\n";
@Override
public BoundSql boundSql(Type type, BoundSql boundSql, CacheKey cacheKey, Chain chain) {
if (type == Type.ORIGINAL) {
String sql = boundSql.getSql();
MetaObject metaObject = MetaObjectUtil.forObject(boundSql);
metaObject.setValue("sql", sql + COMMENT);
}
return chain.doBoundSql(type, boundSql, cacheKey);
}
}
另外,PageHelper.startPage(1, Integer.MAX_VALUE, false).boundSqlInterceptor(...) 这一指定方式还可以起到"不分页、不 count 查询,但执行 Type.ORIGINAL 拦截方法"的作用。
5.2.0 的其他变更:升级 jsqlparser 至 3.2、mybatis 至 3.5.7;自动识别虚谷(xugu)、神通(oscar)、瀚高(highgo)数据库;修复 BoundSqlInterceptorChain 拦截器索引参数 bug;新增 PostgreSQL 方言;修复 total 丢失问题;startRow/endRow 类型由 int 改为 long;Page 新增 public <T> PageInfo<T> toPageInfo(Function<E, T> function) 方法;Oracle 与 Db2 的 ROW_ID 别名改为 PAGEHELPER_ROW_ID 以避免与常用名冲突;支持 ClickHouse 自动识别(按 MySQL 分页);SQL Server 模式替换正则修正,允许 with(nolock) 括号内含空格。
5.5 5.1.10:SQL Server with(nolock) 处理的默认值切换
5.1.0(2017-08-28)引入了处理 SQL Server with (nolock) 问题的 ReplaceSql 接口及 replaceSql 参数,可选值为 simple、regex,或自定义 ReplaceSql 接口实现类全限定名;默认值当时为 simple(沿用旧方式),regex 模式会把 with (nolock) 转换为 table_PAGEWITHNOLOCK 再分页。5.1.10(2019-06-05)将默认值从 simple 切换为 regex,官方评估其可"近乎 100% 解决 SQL Server 分页问题"。两种实现分别对应 SimpleWithNolockReplaceSql 与 RegexWithNolockReplaceSql,解析入口在 SqlServerDialect.java。
变更日志附带了两个真实 issue 的转换示例。第一个(多表 left join 带 nolock):
原始 SQL:
SELECT *
FROM
forum_post_info a with(nolock)
LEFT JOIN forum_carcase_tags as b with(nolock) on a.id = b.carcase_id where b.tag_id = 127
转换后的 Count SQL:
SELECT COUNT(0)
FROM forum_post_info a WITH (NOLOCK)
LEFT JOIN forum_carcase_tags b WITH (NOLOCK) ON a.id = b.carcase_id
WHERE b.tag_id = 127
转换后的分页 SQL:
SELECT TOP 10 *
FROM (
SELECT ROW_NUMBER() OVER (ORDER BY RAND()) AS PAGE_ROW_NUMBER, *
FROM (
SELECT *
FROM forum_post_info a WITH (NOLOCK)
LEFT JOIN forum_carcase_tags b WITH (NOLOCK) ON a.id = b.carcase_id
WHERE b.tag_id = 127
) PAGE_TABLE_ALIAS
) PAGE_TABLE_ALIAS
WHERE PAGE_ROW_NUMBER > 1
ORDER BY PAGE_ROW_NUMBER
第二个示例是一个 7 表 join 的 ACM_User_Schedule 排班查询,所有 with(nolock) 子句同样被规范化为 WITH (NOLOCK) 后完成 count 与 ROW_NUMBER() OVER (ORDER BY RAND()) 分页包裹,转换模式与第一个示例一致。这说明了 regex 模式的价值:分页改写前先做规范化,避免 nolock 子句破坏分页 SQL 的结构解析。
5.6 5.1.6 / 5.1.5:IPage 参数化分页与聚合函数集合
5.1.6(2018-09-05):
- 新增
useSqlserver2012参数:设为true时以 SqlServer2012 方言作为 SQL Server 的默认分页方式,对动态数据源场景有用; - 新增
IPage接口:MyBatis 查询方法的参数实现该接口时,若参数中存在分页参数则自动执行分页查询(当前接口见 IPage.java); - 修复 HashSet 并发问题、优化拦截器代码结构。
5.1.5(2018-09-02)最重要的变更是 aggregateFunctions 参数:默认为各常见数据库的聚合函数集合,允许手动追加聚合函数(影响 count 行数计算)。以聚合函数前缀开头的列会被包裹为子查询,其他函数与列则直接替换为 count(0)。加入该参数前,select ifnull(XXX,YY) from table ... 的 count 查询会被改写为 select count(0) from (select ifnull(xxx,yy) from table ...) temp_count;加入后能区分聚合函数与非聚合函数,非聚合函数场景直接变为更高效的 select count(0) from table ...。
默认的聚合函数前缀集合(与变更日志一致,当前定义在 CountSqlParser.AGGREGATE_FUNCTIONS):
private static final Set<String> AGGREGATE_FUNCTIONS = new HashSet<String>(Arrays.asList(
("APPROX_COUNT_DISTINCT," +
"ARRAY_AGG," +
"AVG," +
"BIT_" +
"BOOL_," +
"CHECKSUM_AGG," +
"COLLECT," +
"CORR," +
"COUNT," +
"COVAR," +
"CUME_DIST," +
"DENSE_RANK," +
"EVERY," +
"FIRST," +
"GROUP," +
"JSON_," +
"LAST," +
"LISTAGG," +
"MAX," +
"MEDIAN," +
"MIN," +
"PERCENT_," +
"RANK," +
"REGR_," +
"SELECTIVITY," +
"STATS_," +
"STD," +
"STRING_AGG," +
"SUM," +
"SYS_OP_ZONE_ID," +
"SYS_XMLAGG," +
"VAR," +
"XMLAGG").split(",")));
(原集合中另有多行被注释掉的 BIT_AND、GROUP_CONCAT、JSON_AGG、STATS_*、VAR_POP 等前缀,源码 CountSqlParser.java 中保留了这些注释,需要时可按需放开。)追加方式见同文件中的 addAggregateFunctions(String functions) 静态方法,支持逗号分隔的多个前缀。
5.7 5.0.x:countSuffix 自定义 count 查询
5.0.4(2017-08-01)新增 countSuffix 自定义 count 查询支持:分页插件会优先查找当前查询 msId + countSuffix 对应的手写分页查询;找不到才按原有方式自动生成 count 查询。示例——存在两个查询:
<select id="selectLeftjoin" resultType="com.github.pagehelper.model.User">
select a.id,b.name,a.py from user a
left join user b on a.id = b.id
order by a.id
</select>
<select id="selectLeftjoin_COUNT" resultType="Long">
select count(distinct a.id) from user a
left join user b on a.id = b.id
</select>
使用 countSuffix 默认值 _COUNT 时,分页插件会自动选用 selectLeftjoin_COUNT 查询。注意:返回值 resultType 必须为 Long,且该查询使用的参数与 selectLeftjoin 相同(SQL 中参数引用方式遵循 selectLeftjoin 的写法);由于是自动调用,接口上无需提供对应方法。执行时的日志输出形如:
DEBUG [main] - ==> Preparing: select count(distinct a.id) from user a left join user b on a.id = b.id
DEBUG [main] - ==> Parameters:
TRACE [main] - <== Columns: C1
TRACE [main] - <== Row: 183
DEBUG [main] - <== Total: 1
DEBUG [main] - ==> Preparing: select a.id,b.name,a.py from user a left join user b on a.id = b.id order by a.id LIMIT 10
5.0.1 同期新增了 countColumn 参数配置自动 count 列(默认 0,即 count(0)),Page 类也可单独配置;5.0.2 中 Page<E> 实现 Closeable,JDK 7+ 可用 try() 自动调用 PageHelper.clearPage();5.0.3 修复 supportMethodsArguments 参数问题。
5.8 5.1.3:defaultCount 与 dialectAlias
5.1.3(2018-04-07):
Page.toString输出格式调整为Page{Attribute}[Collection];- 新增
defaultCount参数:控制默认方法(无 count 查询方法)下是否执行 count 查询,默认true。全局生效,多数据源场景下行为统一。对应源码 PageMethod.java 中DEFAULT_COUNT = Boolean.valueOf(properties.getProperty("defaultCount", "true")); - 新增
dialectAlias参数:为自定义方言实现配置别名,可根据 JDBC URL 自动获取对应实现,从而覆盖已有实现(多个配置用分号分隔):
<property name="dialectAlias" value="oracle=com.github.pagehelper.dialect.helper.OracleDialect"/>
- 新增
PageSerializable类:PageInfo的简化版,信息需求不多时推荐使用,见 PageSerializable.java。
六、关键参数溯源速查表
以下参数均可在 PageInterceptor.setProperties、PageParams、PageAutoDialect、AbstractDialect 等源码位置找到对应的属性读取与初始化逻辑:
| 参数 | 引入版本 | 作用 | 源码落点 |
|---|---|---|---|
countSuffix |
5.0.4 | count 自定义查询后缀,默认 _COUNT |
PageInterceptor |
defaultCount |
5.1.3 | 无手写 count 时是否仍执行 count,默认 true |
PageMethod |
dialectAlias |
5.1.3 | 按 JDBC URL 配置方言别名并覆盖已有实现 | PageAutoDialect |
useSqlserver2012 |
5.1.6 | SQL Server 默认使用 2012 分页语法 | SqlServerDialect |
aggregateFunctions |
5.1.5 | count 时识别的聚合函数前缀集合 | CountSqlParser |
replaceSql |
5.1.0/5.1.10 | SQL Server with(nolock) 处理方式,5.1.10 起默认 regex |
SqlServerDialect |
boundSqlInterceptors |
5.2.0 | BoundSql 拦截器链,逗号分隔多个 | PageBoundSqlInterceptors |
autoDialectClass |
5.3.0 | 数据库类型自动识别实现,如 hikari/druid/old |
PageAutoDialect |
debug |
5.3.2 | 开启后记录调用堆栈 | PageInterceptor |
countMsIdGen |
5.3.2 | 自定义 count msId 生成器 | PageInterceptor |
keepOrderBy / keepSubSelectOrderBy |
5.3.2 | 是否保留 order by / 子查询 order by | PageParams |
asyncCount |
6.0.0 | 全局异步 count,默认 false |
PageParams |
countSqlParser / orderBySqlParser |
6.0.0 / 6.1.0 | 替换 count / orderBy SQL 解析实现 | AbstractDialect |
sqlServerSqlParser |
6.1.0 | 替换 SQL Server 分页解析实现 | SqlServerDialect |
sqlParser(JSqlParser) |
5.3.2 | JSqlParser 与 JDK 兼容配置,6.1.0 已移除 | — |
pageSizeZero / reasonable / supportMethodsArguments / countColumn / params |
更早版本 | 页码/分页/参数行为控制 | PageParams |
七、升级建议与兼容性注意事项
- JDK 要求:6.0 起仅支持 JDK 8+(当前构建配置即 JDK 8),JDK 6/7 环境请停留在 5.x;
- Spring Boot 3.x / Jakarta EE 用户:优先升级到 6.1.1,该版本修复了 Jakarta/Javax
ServletRequest兼容问题; - SunDB、虚谷数据库用户:6.1.1 起可直接使用分页功能;
- JSqlParser 版本冲突:6.1.0 起插件不再内嵌解析器。若宿主项目锁定了 jsqlparser 4.5,请通过"排除 + 引入
sqlparser4.5"的方式切换实现;若需 JSqlParser 4.9/5.x 系列,使用 6.1.1 的 sqlparser4.9 / sqlparser5.1 模块;解析超时问题可叠加sqlparser-timeout; - 安全相关:5.3.1 修复了 CVE-2022-28111(orderBy 注入),6.0.0 为
countColumn增加注入检测,涉及外部可控排序/列参数的系统建议升级到这些版本之后; JSqlParser接口与sqlParser参数:在 6.1.0 中已移除,升级时如配置了该参数需一并清理;- 各参数的详细配置说明以官方使用文档 wikis/en/HowToUse.md(英文)与 wikis/zh/HowToUse.md(中文)为准,拦截器细节可参考 wikis/zh/Interceptor.md。
八、继续深入
- 变更日志原文(英文):wikis/en/Changelog.md;更早版本的中文日志:wikis/zh/Changelog.md;
- 插件主拦截器与配置解析:PageInterceptor.java;
- SQL 解析 SPI 架构:SqlParser.java、SqlParserUtil.java、CountSqlParser.java、OrderBySqlParser.java;
- BoundSql 拦截器:BoundSqlInterceptor.java、BoundSqlInterceptorChain.java;
- 方言与自动识别:dialect(含 auto、helper、rowbounds 子包);
- 构建与发布配置:pom.xml。