新闻详情

新闻详情

首页 / 资讯中心 / 详情

PageHelper 分页框架查询总数 SQL 错误解决方案:从源码逻辑到版本影响(含实验验证)

发布时间:2026/11/9 17:26:14来源:尧图网络
PageHelper 分页框架查询总数 SQL 错误解决方案:从源码逻辑到版本影响(含实验验证)
目录一、问题背景与环境信息二、问题复现实验一2.1 测试代码XML 映射文件SQL 语句Java 调用代码2.2 运行报错与异常日志2.3 核心疑问三、问题原因分析基于源码3.1 PageHelper 对ORDER BY的处理逻辑3.2 PageHelper 生成总条数 SQL 的判断逻辑3.3 最终错误成因四、解决方案4.1 核心原理4.2 修改后的代码XML五、补充实验与版本影响实验二5.1 实验二布尔类型ORDER BY的特殊情况测试 SQL不再使用case when 来进行排序布尔排序指定校区优先降序不同版本下的结果对比5.2 版本影响结论六、总结一、问题背景与环境信息本文针对 PageHelper 分页框架在生成查询总数 SQL 时出现的语法错误问题展开分析涉及工具及版本信息如下PageHelper 版本5.1.8后续补充 6.6.1 版本对比测试JSqlParser 版本1.2PageHelper 5.1.x 默认依赖一个用于解析 SQL 语句的 Java 库PageHelper 6.6.1 升级为 4.7 版本数据库PostgreSQL11.1核心问题含特殊ORDER BY子句的查询 SQL生成总数统计 SQL 时未正确处理导致报 “字段需 GROUP BY” 错误。该问题已被 PageHelper 采纳并修复详情可以看我提的PRGitHub PR地址二、问题复现实验一2.1 测试代码XML 映射文件SQL 语句select idselectTest parameterTypecom.jiuaoedu.serviceprofile.pojo.student.StudentDetail resultMapBaseResultMap select * from service_profile.student s -- 按“指定校区优先0→其他校区1”排序再按校区ID降序NULL值后置 order by CASE WHEN s.school_area #{schoolArea} THEN 0 ELSE 1 END, s.school_area DESC NULLS LAST /selectJava 调用代码PageInfoStudentDetail pageInfo PageHelper.startPage(pageNum, pageSize).doSelectPageInfo( () - studentDetailMapper.selectTest(student) );2.2 运行报错与异常日志生成的错误总数 SQLSELECT count(0) FROM service_profile.student s ORDER BY CASE WHEN s.school_area ? THEN 0 ELSE 1 END, s.school_area DESC NULLS LAST报错原因COUNT(0)聚合查询中包含ORDER BY子句且school_area未参与GROUP BY违反 SQL 语法规则。2.3 核心疑问正常情况下 PageHelper 会过滤ORDER BY子句以提升计数性能为何本次未过滤为何未生成 “外层COUNT嵌套原查询” 的正确 SQL如下反而直接将*替换为count(0)sqlSELECT count(0) FROM (select * FROM service_profile.student s ORDER BY ...) tmp_count三、问题原因分析基于源码PageHelper 分页核心流程分为两步1. 查询总条数2. 总条数非 0 时执行分页查询。错误根源在于 “生成总条数 SQL” 的逻辑处理。3.1 PageHelper 对ORDER BY的处理逻辑核心源码片段orderByHashParameters方法逻辑结论若ORDER BY子句包含占位符如实验一中的#{schoolArea}对应?PageHelper 会保留ORDER BY不会过滤 —— 这解释了 “疑问 1”。3.2 PageHelper 生成总条数 SQL 的判断逻辑核心源码片段isSimpleCount方法与sqlToCount方法/** * 判断是否为“简单查询”决定是否直接替换查询列为count(0) * param select 简单查询对象PlainSelect * return 是简单查询返回true否则false */ public boolean isSimpleCount(PlainSelect select) { // 1. 含GROUP BY → 非简单查询 if (select.getGroupByColumnReferences() ! null) { return false; } // 2. 含DISTINCT → 非简单查询 if (select.getDistinct() ! null) { return false; } // 3. SELECT列含占位符 → 非简单查询 for (SelectItem item : select.getSelectItems()) { if (item.toString().contains(?)) { return false; } // 4. SELECT列含聚合函数非允许列表→ 非简单查询 if (item instanceof SelectExpressionItem) { Expression expression ((SelectExpressionItem) item).getExpression(); if (expression instanceof Function) { // 聚合函数如SUM、AVG判断逻辑... } } } return true; } /** * 将原查询SQL转换为总条数SQL */ public void sqlToCount(Select select, String name) { SelectBody selectBody select.getSelectBody(); ListSelectItem COUNT_ITEM new ArrayList(); COUNT_ITEM.add(new SelectExpressionItem(new Function(count, new Column(name)))); // 若为简单查询直接替换SELECT列为count(0) if (selectBody instanceof PlainSelect isSimpleCount((PlainSelect) selectBody)) { ((PlainSelect) selectBody).setSelectItems(COUNT_ITEM); } else { // 非简单查询生成“外层COUNT嵌套原查询”的SQL PlainSelect plainSelect new PlainSelect(); SubSelect subSelect new SubSelect(); subSelect.setSelectBody(selectBody); subSelect.setAlias(tmp_count); plainSelect.setFromItem(subSelect); plainSelect.setSelectItems(COUNT_ITEM); select.setSelectBody(plainSelect); } }包含GROUP BY子句因为GROUP BY会使结果聚合不再是简单计数包含DISTINCT关键字DISTINCT会去重影响计数结果SELECT列表中包含参数用?表示参数可能会导致执行计划不稳定SELECT列表中包含聚合函数如SUM、AVG等这些函数会改变计数逻辑实验截图如果该查询是一个简单的查询就将sql的查询列重置为count(0)逻辑结论实验一中的原查询满足 “简单查询” 条件无GROUP BY、DISTINCTSELECT列仅为*不含占位符 / 聚合函数因此 PageHelper 直接将*替换为count(0)未生成嵌套查询 —— 这解释了 “疑问 2”。3.3 最终错误成因保留ORDER BY因含占位符 简单查询直接替换count(0)两者叠加生成了 “COUNTORDER BY” 的错误 SQL。四、解决方案4.1 核心原理PageHelper 源码中存在特殊注释标识/*keep orderby*/若 SQL 中包含该注释会强制生成 “外层COUNT嵌套原查询” 的 SQL跳过直接替换逻辑避免错误。对应源码片段getSmartCountSql方法public String getSmartCountSql(String sql, String name) { // 若SQL含/*keep orderby*/直接生成嵌套COUNT查询 if (sql.indexOf(/*keep orderby*/) 0) { return getSimpleCountSql(sql, name); } // 其他解析逻辑... } /** * 生成“外层COUNT嵌套原查询”的SQL */ public String getSimpleCountSql(final String sql, String name) { StringBuilder sb new StringBuilder(sql.length() 40); sb.append(select count().append(name).append() from (); sb.append(sql); sb.append() tmp_count); return sb.toString(); }4.2 修改后的代码XMLselect idselectTest parameterTypecom.jiuaoedu.serviceprofile.pojo.student.StudentDetail resultMapBaseResultMap select * from service_profile.student s /*keep orderby*/ -- 关键注释强制生成嵌套COUNT查询 order by CASE WHEN s.school_area #{schoolArea} THEN 0 ELSE 1 END, s.school_area DESC NULLS LAST /select实验截图该 SQL 符合语法规则可正常执行计数分页功能恢复正常。五、补充实验与版本影响实验二5.1 实验二布尔类型ORDER BY的特殊情况测试 SQL不再使用case when 来进行排序布尔排序指定校区优先降序select idselectTest parameterTypecom.jiuaoedu.serviceprofile.pojo.student.StudentDetail resultMapBaseResultMap select * from service_profile.student s order by s.school_area #{schoolArea} desc nulls last /select按照之前的分析结果来看应该会报错并且生成的sql应该如下select count(0) from service_profile.student s order by s.school_area ? desc nulls last按照之前的分析结果来看应该会报错并且生成的sql应该如下select count(0) from service_profile.student s order by s.school_area ? desc nulls last但是事实却是查询正确生成的查询总条数sql如下select count(0) from (select * from service_profile.student s order by s.school_area ? desc nulls last) tmp_count到这儿我就懵了不应该如此啊接着debug。看到这里我就知道了PageHelper中引入的jsqlparser较低jsqlparser解析不了该sql报错然后就直接返回了simpleCountSql我升级了pageHelper版本至最新版本6.6.1再次尝试上诉所有内容实验结果实验1跟第一次没升级版本出现的报错一样实验2第一次没升级不会报错正常分页查询。在升级版本之后却出现了报错原因是因为pageHelper6.6.1中jsqlparser升级为了4.7能够正常解析实验2的结果然后就出现了和实验1一样的报错。不同版本下的结果对比PageHelper 版本JSqlParser 版本执行结果原因分析5.1.81.2正常计数JSqlParser 1.2 无法解析 “布尔排序” SQL解析报错后触发降级逻辑自动生成嵌套 COUNT 查询6.6.14.7报错同实验一JSqlParser 4.7 可正常解析 “布尔排序” SQL进入 “简单查询 保留 ORDER BY” 逻辑生成错误 SQL5.2 版本影响结论PageHelper 5.1.8低版本 JSqlParser部分复杂ORDER BY因解析失败可能 “意外正常”但稳定性差。PageHelper 6.6.1高版本 JSqlParser解析能力增强更多ORDER BY会被保留需主动添加/*keep orderby*/避免错误。六、总结错误根源含占位符的ORDER BY被保留 简单查询直接替换count(0)导致 SQL 语法错误。通用解决方案在含特殊ORDER BY含占位符、布尔排序等的查询 SQL 中添加/*keep orderby*/注释强制生成嵌套 COUNT 查询。版本建议升级 PageHelper 后需重点检查ORDER BY相关查询确保添加该注释避免因 JSqlParser 解析能力提升导致新错误。
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

【外设】之大彩串口显示屏 2026/11/8 22:42:07

【外设】之大彩串口显示屏

大彩串口屏初步使用 1 .官网下载 STM32 屏幕 GUI 设计资料 http://www.gz-dc.com/category/typeid/4112 找到 STM32 Keil 工程,移植相关代码因项目而异进行移植,由于项目简单,本人只对用到的指令接口进行修改。 比如:注意事项&…

阅读更多 →
无法下载Windows系统iso文件 2026/11/9 8:37:19

无法下载Windows系统iso文件

当我遇到这个问题的时候,我打开了一个网站: 登录 然后我打算下载的时候: 突然那个官方的连接就可以下载了:

阅读更多 →
【清华代码熊】DeepSeek V4.1 Flash 后训练详解 2026/11/9 8:37:19

【清华代码熊】DeepSeek V4.1 Flash 后训练详解

📌 上期解析了 DeepSeek V4.1 Flash 模型架构改进,本期解析 DeepSeek V4.1 Flash 预训练/后训练技术: 🌟 预训练:45T 文本 多模态混合语料、直接训练 sparse attention(取消 DeepSeek V4 的 dense 冷启动&…

阅读更多 →
Shuffle-R1: Efficient RL framework for Multimodal Large Language Models via Data-centric Dynamic ... 2026/11/8 22:41:24

Shuffle-R1: Efficient RL framework for Multimodal Large Language Models via Data-centric Dynamic ...

文章主要内容和创新点 主要内容 本文聚焦于多模态大语言模型(MLLM)强化学习(RL)训练中的效率问题,提出了一个名为Shuffle-R1的框架。研究发现,当前RL训练存在两个关键缺陷: 优势值坍缩(Advantage Collapsing):批次中大多数优势值集中在零附近,导致有效梯度信号被淹…

阅读更多 →
PRvL: Quantifying the Capabilities and Risks of Large Language Models for PII Redaction 2026/11/9 8:37:18

PRvL: Quantifying the Capabilities and Risks of Large Language Models for PII Redaction

一、文章主要内容总结 本文聚焦于利用大型语言模型(LLMs)实现个人身份信息(PII)脱敏的研究,旨在解决传统脱敏方法(如基于规则的系统、领域特定命名实体识别(NER)模型)泛化能力差、跨格式/跨语境适应性弱的问题。 研究通过全面评估多种LLM架构(包括密集型LLM(D-LLM…

阅读更多 →
LLaVA-RE: Binary Image-Text Relevancy Evaluation with Multimodal Large Language Model 2026/11/9 8:37:18

LLaVA-RE: Binary Image-Text Relevancy Evaluation with Multimodal Large Language Model

文章主要内容和创新点 主要内容 本文聚焦于二进制图像-文本相关性评估任务(判断图像与文本“相关”或“不相关”),针对该任务中文本格式多样、相关性定义随场景变化等挑战,提出了基于多模态大语言模型(MLLM)的解决方案LLaVA-RE。 模型设计:LLaVA-RE基于LLaVA 1.5架构,…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉