PageHelper分页失效?5个常见坑点及解决方案(附真实案例)
PageHelper分页失效?5个常见坑点及解决方案(附真实案例)
在MyBatis开发中,PageHelper作为最流行的分页插件,极大简化了分页查询的实现。然而在实际项目中,开发者常常遇到分页失效的"灵异事件"——明明调用了startPage方法,返回的却是全部数据。本文将深入分析5种典型场景,通过真实案例还原问题本质,并提供可直接落地的解决方案。
1. 集合操作导致分页参数丢失
典型场景:对分页查询结果进行二次处理时,无意中破坏了分页结构。例如:
// 错误示例 PageHelper.startPage(1, 10); List<Product> products = productDAO.listProducts(); products.add(new Product()); // 添加元素 products.remove(0); // 删除元素 return new PageInfo<>(products);问题分析:
PageInfo构造函数要求传入的集合必须是原始分页查询结果- 任何对集合的增删操作都会导致分页元信息(总条数、页码等)计算错误
- 返回的
PageInfo对象中页码信息可能显示正确,但实际数据条数与pageSize不符
解决方案:
// 正确做法 PageHelper.startPage(1, 10); List<Product> originList = productDAO.listProducts(); List<Product> processedList = originList.stream() .map(this::processProduct) .collect(Collectors.toList()); PageInfo<Product> pageInfo = new PageInfo<>(originList); pageInfo.setList(processedList); return pageInfo;关键点:保持原始查询集合不变,通过
setList方法注入处理后的数据
2. 线程池引发的ThreadLocal污染
并发场景下的经典问题:
@GetMapping("/concurrent") public void concurrentTest() { threadPool.execute(() -> { PageHelper.startPage(1, 10); // 分页参数存入ThreadLocal productDAO.listProducts(); // 正常分页查询 }); // 线程复用导致分页参数泄露 threadPool.execute(() -> { productDAO.listProducts(); // 意外被分页! }); }问题本质:
- PageHelper通过ThreadLocal存储分页参数
- 线程池复用线程时未清理ThreadLocal
- 后续查询意外继承分页参数
防御方案:
| 方案类型 | 实现方式 | 适用场景 |
|---|---|---|
| 同步清理 | PageHelper.clearPage() | 简单异步场景 |
| 装饰线程 | ThreadLocalUtil.wrapRunnable() | 复杂线程池环境 |
| Lambda语法 | doSelectPage(() -> {...}) | JDK8+项目 |
最佳实践:
// 使用Lambda避免内存泄漏 Page<Product> page = PageHelper.startPage(1, 10) .doSelectPage(() -> productDAO.listProducts());3. 分页拦截器配置异常
常见配置问题:
- 未正确注册拦截器
- 多数据源场景配置冲突
- 特殊SQL语法不支持
Spring Boot配置要点:
# application.yml pagehelper: helper-dialect: mysql reasonable: true support-methods-arguments: true params: count=countSql多数据源注意事项:
- 明确指定
autoRuntimeDialect: true - 不同数据源使用不同dialect别名
- 避免重复注册拦截器
排查工具:在日志中搜索
PageHelper关键词,确认拦截器是否生效
4. 特殊查询场景限制
PageHelper对某些特殊查询存在兼容性问题:
不支持的情况:
- 包含
FOR UPDATE的锁定查询 - 嵌套结果映射(Nested ResultMap)
- 存储过程调用
- 使用
UNION的复合查询
替代方案:
/* 原生分页查询示例 */ SELECT * FROM ( SELECT id, name FROM product WHERE status = 1 ORDER BY create_time DESC ) temp LIMIT 0, 105. 分页参数边界条件
容易忽略的细节:
pageSize=0时会返回全部数据reasonable参数对页码的自动修正- 参数传递类型不匹配
参数处理最佳实践:
public PageInfo<Product> queryProducts(PageParam param) { // 参数校验 if (param.getPageSize() <= 0) { param.setPageSize(10); // 默认值 } // 分页查询 PageHelper.startPage(param.getPageNum(), param.getPageSize()); List<Product> list = productDAO.queryByCondition(param); return new PageInfo<>(list); }关键检查点:
- 确认传入参数是否为基本类型(避免Integer判空问题)
- 检查是否配置了
pageSizeZero参数 - 验证
reasonable参数是否符合业务预期
在实际项目中遇到分页问题时,建议按照以下流程排查:
- 检查SQL日志确认是否生成LIMIT子句
- 验证ThreadLocal是否及时清理
- 检查返回集合是否为原始查询结果
- 确认拦截器配置是否正确加载
- 排除特殊SQL语法限制
