Mybatis-PageHelper 版本演进全解:从 5.0.0 到 6.1.1 的功能变更、参数溯源与升级指南

原创2026-10-13 17:19:151,941 阅读
文章标签:后端ORM

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 方法。执行时序分三种情况:

  1. 无论当前 SQL 是否会被分页,Type.ORIGINAL 类型的拦截方法都会执行;
  2. 调用分页方法时,继续执行 Type.COUNT_SQL 拦截方法——仅当执行分页且执行 count 查询时触发;
  3. 调用分页方法且 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

七、升级建议与兼容性注意事项

  1. JDK 要求:6.0 起仅支持 JDK 8+(当前构建配置即 JDK 8),JDK 6/7 环境请停留在 5.x;
  2. Spring Boot 3.x / Jakarta EE 用户:优先升级到 6.1.1,该版本修复了 Jakarta/Javax ServletRequest 兼容问题;
  3. SunDB、虚谷数据库用户:6.1.1 起可直接使用分页功能;
  4. JSqlParser 版本冲突:6.1.0 起插件不再内嵌解析器。若宿主项目锁定了 jsqlparser 4.5,请通过"排除 + 引入 sqlparser4.5"的方式切换实现;若需 JSqlParser 4.9/5.x 系列,使用 6.1.1 的 sqlparser4.9 / sqlparser5.1 模块;解析超时问题可叠加 sqlparser-timeout;
  5. 安全相关:5.3.1 修复了 CVE-2022-28111(orderBy 注入),6.0.0 为 countColumn 增加注入检测,涉及外部可控排序/列参数的系统建议升级到这些版本之后;
  6. JSqlParser 接口与 sqlParser 参数:在 6.1.0 中已移除,升级时如配置了该参数需一并清理;
  7. 各参数的详细配置说明以官方使用文档 wikis/en/HowToUse.md(英文)与 wikis/zh/HowToUse.md(中文)为准,拦截器细节可参考 wikis/zh/Interceptor.md。

八、继续深入

登录后查看全文
Mybatis-PageHelper