PageHelper是MyBatis生态中实现高效物理分页的核心插件,通过拦截SQL并动态添加数据库原生分页语法(如MySQL的LIMIT),在数据库层面只查询当前页数据,避免全表加载与内存截取,显著降低IO和内存开销。

MyBatis 配合 PageHelper 实现高效物理分页,关键在于不改 SQL、不查全表、只取当前页数据。它不是在 Java 层截取 List,而是在数据库执行时就加上 LIMIT(MySQL)或 ROW_NUMBER(Oracle)等原生分页语法,大幅降低 IO 和内存开销。
一、基础配置要到位
PageHelper 依赖拦截器机制,必须正确注册才能生效:
- 引入 Maven 依赖(5.3.0+ 推荐,自动带 jsqlparser):
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.3.3</version>
</dependency> - Spring Boot 项目:在
application.yml中配置方言和常用参数:
pagehelper:
helper-dialect: mysql
reasonable: true # 页码超范围时自动修正为 1 或最后一页
support-methods-arguments: true # 允许通过 Mapper 方法参数传 pageNum/pageSize
params: count=countSql # 指定 count 查询标识符,避免误判 - 非 Spring Boot 项目:在
mybatis-config.xml的<plugins>标签下配置PageInterceptor,注意插件位置必须在<environments>之前。
二、分页调用要规范
调用时机和方式直接影响是否真正走物理分页:
-
必须在查询前调用
PageHelper.startPage(),且紧邻 Mapper 执行语句(中间不能穿插其他查询或事务操作); - 推荐写法:
PageHelper.startPage(2, 15);
List<Order> list = orderMapper.selectByStatus("PAID");
PageInfo<Order> pageInfo = new PageInfo<>(list); - 支持链式调用排序:
PageHelper.startPage(1, 20).setOrderBy("create_time DESC, id ASC"); - 如只需分页数据、不要总条数(例如滚动加载场景),用三参数版:
PageHelper.startPage(1, 20, false); // 第三个参数 false 表示跳过 COUNT 查询
三、结果封装要用 PageInfo
PageInfo 不只是包装 List,它把分页元信息一次性全带回来,前端可直接渲染:
立即学习“Java免费学习笔记(深入)”;
-
pageInfo.getTotal()—— 总记录数(来自自动触发的 COUNT 查询); -
pageInfo.getPages()—— 总页数; -
pageInfo.isFirstPage()/pageInfo.isLastPage()—— 判断边界; -
pageInfo.getNavigatepageNums()—— 返回 [1,2,3,4,5] 这类导航页码数组,适配分页控件; - 注意:
pageInfo.getList()就是当前页的实体列表,无需再手动 subList。
四、避坑要点记清楚
很多“分页失效”或“数据错乱”都源于这几个细节:
-
严禁混用 RowBounds:MyBatis 原生的
RowBounds是内存分页,与 PageHelper 冲突,会导致 LIMIT 被重复拼接或失效; -
多表联查慎用 count:复杂 JOIN 场景下自动生成的 COUNT SQL 可能性能差甚至出错,建议手写
count查询并用@SelectProvider或<select id="xxxCount">显式指定; -
ThreadLocal 生命周期要清理:尤其在异步线程或线程复用场景(如 Dubbo Filter、定时任务),记得调用
PageHelper.clearPage()防止参数泄漏; -
深度分页慎用:如
LIMIT 1000000, 20在 MySQL 中仍需扫描前 100 万行,建议改用游标分页(如 last_id)或时间范围分页。


















