当前位置: 首页 > news >正文

一次由 PageHelper 分页污染引发的 Bug 排查实录

一次由 PageHelper 分页污染引发的 Bug 排查实录

  • 一、问题背景
  • 二、问题现象
  • 三、深入排查
    • 1. 复现现象
    • 2. 问题根源:PageHelper 的全局分页上下文
  • 四、最终解决方案
    • 1. 调整分页时机
    • 2. 显式清除分页上下文
  • 五、优化与扩展建议
    • 1. 避免在分页期间执行其他 SQL
    • 2. 使用独立事务或异步校验
    • 3. 使用 MyBatis-Plus 或自定义分页参数
    • 4. 在项目统一分页入口中加入清理逻辑
  • 六、总结


——从「查不到用户」到「分页上下文污染」,一次看似离谱的排查之旅

在这里插入图片描述


一、问题背景

在开发一个定制资源管理模块时,有这样一个场景:

管理员用户可以分页查看单位下的所有定制数据。

分页查询逻辑中,会先校验当前用户的角色权限(是否为管理员),再去执行分页查询列表。

相关伪代码如下:

public PageResult getCustomizeList(SelectCustomizeDTO dto) {// 分页配置PageHelper.startPage(dto.getPage(), dto.getSize());// 权限校验(查询单位 + 用户角色信息)validateUserPermission(dto.getFid(), dto.getUid(), allowRoleTypes, true);// 分页查询数据List<CustomizeVO> customizeList = customMapper.getCustomizeList(...);return new PageResult(new PageInfo<>(customizeList));
}

逻辑很正常,看上去没问题。

但在上线后,却遇到了一个非常诡异的 Bug。


二、问题现象

当管理员访问数据列表时:

  • 第一页数据正常显示

  • 第二页开始,直接报错:用户在单位下,无角色信息

然而在数据库中手动执行 SQL:

SELECT role_type FROM user_manage WHERE fid = 123 AND uid = 111111;

结果明明存在,角色信息完全正常。


三、深入排查

1. 复现现象

通过调试发现:

==> Preparing: SELECT count(0) FROM user_manage WHERE del_flag = 0 AND fid = ? AND userid = ?
==> Parameters: 123(Integer), 111111(Long)
<== Columns: count(0)
<== Row: 1

注意!MyBatis 打印的 SQL 不是原本的 SELECT role_type ...,而是 SELECT count(0)

这说明——分页插件 PageHelper 正在拦截这个查询!


2. 问题根源:PageHelper 的全局分页上下文

PageHelper 的分页实现方式是通过 ThreadLocal 保存分页参数:

PageHelper.startPage(page, size);

一旦调用,它会将分页信息绑定到当前线程上。

后续同线程内的所有查询语句,都会被它拦截,自动改写为 SELECT count(0)limit ... 的分页 SQL。

在本案例中:

  • 先执行了 PageHelper.startPage()

  • 再执行了 validateUserPermission(),该方法内部调用了 userManageMapper.getUserRoleType(fid, uid)

  • 这个查询被分页插件错误地分页了!

于是变成:

SELECT count(0) FROM user_manage WHERE del_flag = 0 AND fid = ? AND uid = ?;

执行结果当然只有一条计数结果,

自然无法映射到 Integer roleType 字段中,导致角色信息为 null,触发“用户无角色”的异常。


四、最终解决方案

1. 调整分页时机

只需将分页逻辑移动到权限校验之后:

public PageResult getCustomizeList(SelectCustomizeDTO dto) {// 先做权限校验validateUserPermission(dto.getFid(), dto.getUid(), allowRoleTypes, true);// 再开启分页PageHelper.startPage(dto.getPage(), dto.getSize());List<CustomizeVO> customizeList = customMapper.getCustomizeList(...);return new PageResult(new PageInfo<>(customizeList));
}

2. 显式清除分页上下文

如果确实需要在分页开启之后再调用其他 SQL(比如复杂场景),

可以使用:

PageHelper.clearPage(); // 清除 ThreadLocal 分页上下文
validateUserPermission(fid, uid, allowRoleTypes, true);
PageHelper.startPage(page, size); // 再次启用分页

这样只让后续的分页查询生效,而不影响权限验证、统计查询等逻辑。


五、优化与扩展建议

1. 避免在分页期间执行其他 SQL

分页设置与业务 SQL 解耦是最简单的方案。
建议做到:

  • 只在真正分页查询前调用 startPage()

  • 任何业务前置检查(如权限、统计、过滤)都应在分页之前完成。

2. 使用独立事务或异步校验

如果权限逻辑较复杂,可以单独封装成一个独立的 Service 方法并加上 @Transactional(propagation = Propagation.REQUIRES_NEW)

这样在独立事务中执行,不受外部分页或线程上下文影响。

3. 使用 MyBatis-Plus 或自定义分页参数

MyBatis-Plus 的分页拦截器使用 Page 对象,不依赖 ThreadLocal,因此天然不会污染其他查询。

例如:

IPage<CustomizeVO> page = new Page<>(dto.getPage(), dto.getSize());
customMapper.selectPage(page, new QueryWrapper<>());

4. 在项目统一分页入口中加入清理逻辑

可以在 PageHelper.startPage() 之前先执行一次:

PageHelper.clearPage();

防止前一层(如拦截器或 AOP)残留的分页上下文干扰当前请求。


六、总结

问题现象根因解决方案
第1页正常,第2页开始查询不到用户PageHelper 分页污染了权限 SQL调整分页时机或使用 PageHelper.clearPage()
MyBatis 日志中出现 SELECT count(0)分页上下文仍在作用中清除或重启分页上下文
多层调用混合分页与非分页查询ThreadLocal 未隔离使用新事务或 MyBatis-Plus 分页

⚙️ 一句话总结
PageHelper 的分页配置是「线程级全局状态」,
一旦开启,同线程内的所有 SQL 都会受影响,除非手动清除。


写在最后
这个问题看似偶发,其实在微服务或复杂多层调用场景中非常常见。

经验教训是:

👉 分页要“就地调用”,

👉 权限校验、统计查询要“避开分页线程”,
👉 出现奇怪的 SELECT count(0) 时,第一反应就是:是不是 PageHelper 干的。


http://www.dtcms.com/a/581117.html

相关文章:

  • 记录一个IDEA的神奇bug
  • Spark-3.5.7文档3 - Spark SQL、DataFrame 和 Dataset 指南
  • 无需 iTunes,将 iPhone 语音备忘录传输到电脑
  • 三个好思路:SQL并行化处理、混淆矩阵和特征交叉
  • 5 种无需 iTunes 将 iPad 照片传输到电脑的方法
  • 网站制作网站设计自助建站网站程序源码
  • Jenkins 定时触发(cron)使用说明
  • Kubernetes 架构
  • 自己做有趣的网站娱乐网站名字
  • 黑马JAVAWeb-09 文件上传-文件存储到服务器本地磁盘-文件存储在阿里云
  • 医疗小程序04添加就诊人
  • uboot下查看分区
  • 微信小程序camera相机帧转图片base64
  • Agentic AI基础设施实践经验系列(四):MCP服务器从本地到云端的部署演进
  • Linux系统性基础学习笔记
  • DDR5 DFE(Decision Feedback Equalizer)
  • 前程无忧企业官方网站logo制作下载
  • 做网站难学吗wordpress替换百度站内搜索
  • STM32项目分享:基于单片机的空气质量检测系统设计
  • Windows 下PostgreSQL 数据库相关及 n8n .env文件的配置
  • jsp与网站开发期末试题做调查问卷赚钱哪个网站好
  • 在Centos7.9上安装配置zabbix proxy保姆级教程
  • 万能近似定理:神经网络「拟合万物」的理论基石
  • autofs自动挂载
  • 微软TinyTroupe“人格”模拟库:AI智能体市场调研-V3版本(五)
  • Opencv(九) : 图像旋转
  • 关键词解释:DAG 系统(Directed Acyclic Graph,有向无环图)
  • 【Linux】基础开发⼯具
  • 那些网站可以给产品做推广个人网站备案填写
  • 现代汽车确认遭遇数据泄露, 攻击者连续窃密9天获取用户驾照信息