MyBatis分页神器PageHelper深度解析
PageHelper 是一个优秀的 MyBatis 分页插件,它通过简单的拦截器机制,实现了对 MyBatis 查询的物理分页(而非内存分页),极大简化了分页代码的编写。而 PageHelper 扩展 通常指的是在其核心功能基础上,为特定框架(如 Spring Boot)或特定需求(如简化配置、增强功能)提供的增强模块或使用方式。
以下是对 PageHelper 及其常见扩展的介绍:
一、核心 PageHelper 功能回顾
- 核心原理:
- 基于 MyBatis 的
Interceptor接口,拦截 Executor 的查询方法。 - 在执行目标 SQL 前,自动分析原 SQL 并生成
COUNT查询(获取总数)和添加了物理分页(如LIMIT,ROWNUM)的查询。
- 基于 MyBatis 的
- 基本用法:
// 在查询方法前调用,设置分页参数 PageHelper.startPage(pageNum, pageSize); // pageNum: 页码, pageSize: 每页条数 // 紧接着的第一个 MyBatis 查询方法会被分页 List<Country> list = countryMapper.selectAll(); // 用 PageInfo 包装结果,包含分页详细信息(总记录数、总页数、当前页等) PageInfo<Country> pageInfo = new PageInfo<>(list); - 主要优点:
- 简单:一行代码启动分页。
- 高效:物理分页,数据库压力小。
- 通用:支持多种数据库(MySQL, Oracle, PostgreSQL, SQLServer 等)。
- 灵活:支持多种参数传递方式(
startPage方法、RowBounds参数)。 - 丰富信息:
PageInfo对象提供全面的分页信息。
二、重要的 PageHelper 扩展
-
pagehelper-spring-boot-starter- 定位:官方提供的 Spring Boot 自动配置 Starter。
- 核心价值:
- 零配置/简化配置:只需引入依赖,大部分情况下无需任何额外配置即可使用。
- 自动注入:自动配置
PageHelper拦截器并将其注册到 MyBatisSqlSessionFactory中。 - 约定优于配置:遵循 Spring Boot 的配置习惯,可以通过
application.properties/application.yml轻松配置插件属性。
- 依赖 (Maven):
<dependency><groupId>com.github.pagehelper</groupId><artifactId>pagehelper-spring-boot-starter</artifactId><version>最新版本</version> <!-- 请替换为当前最新版本,如 2.1.0 --> </dependency> - 常用配置示例 (
application.yml):pagehelper:helper-dialect: mysql # 指定数据库方言(通常可自动检测)reasonable: true # 启用合理化:pageNum<=0 时设为 1,pageNum>总页数时设为最后一页support-methods-arguments: true # 支持通过 Mapper 接口参数传递分页参数params: count=countSql # 配置 COUNT 查询的返回值 keypage-size-zero: true # 允许 pageSize=0 时查询所有结果(返回 PageInfo,total=实际条数) - 使用:在 Spring Boot 项目中引入该 starter 后,直接在 Service 或 Controller 中使用
PageHelper.startPage(pageNum, pageSize)即可。
-
PageHelper的参数模式扩展- 核心功能扩展:除了
PageHelper.startPage,PageHelper本身支持更灵活的参数传递:- 方法参数传递 (需配置
support-methods-arguments: true):// Mapper 接口 List<User> selectUsers(@Param("name") String name, @Param("pageNum") int pageNum, @Param("pageSize") int pageSize);// Service 调用 (无需显式调用 startPage) List<User> users = userMapper.selectUsers("John", 2, 10); PageInfo<User> pageInfo = new PageInfo<>(users); RowBounds参数传递 (较旧方式,优先级低于startPage):List<User> users = sqlSession.selectList("selectUsers", null, new RowBounds(2, 10));
- 方法参数传递 (需配置
- 核心功能扩展:除了
-
PageInfo的增强- 虽然
PageInfo本身是核心的一部分,但它提供的丰富分页信息是其强大扩展性的体现:getTotal():总记录数。getPages():总页数。getPageNum():当前页码。getPageSize():每页条数。getList():当前页的数据列表。isIsFirstPage()/isIsLastPage():是否第一页/最后一页。hasPreviousPage()/hasNextPage():是否有上一页/下一页。getPrePage()/getNextPage():上一页/下一页页码。getNavigatepageNums():所有导航页码(如 [1,2,3,4,5])。
- 这极大地简化了将分页信息返回给前端的工作。
- 虽然
三、使用 PageHelper 扩展的注意事项
PageHelper.startPage的调用位置:- 必须紧挨着需要分页的 MyBatis 查询方法调用之前。中间不能有其它可能触发查询的操作(如调用另一个查询方法)。
- 线程安全:
PageHelper.startPage内部使用ThreadLocal保存分页参数。这意味着它是线程安全的,但也意味着分页参数只对当前线程的紧接着的下一次查询有效。查询完成后,ThreadLocal会被自动清理。在异步、多线程或复杂调用链中要特别注意调用时机。
PageInfo的构造:- 传入
PageInfo构造器的List对象,必须是PageHelper.startPage后执行分页查询返回的那个List。这个List实际上是一个Page对象(实现了List接口),包含了分页信息。如果用其他List构造PageInfo,信息会错误。
- 传入
- 数据库方言 (
dialect):- 确保配置正确的数据库方言,或让插件自动检测(通常可行)。错误的方言会导致生成错误的分页 SQL。
reasonable参数:- 建议开启 (
reasonable: true),避免用户传入非法页码导致空数据或错误。
- 建议开启 (
page-size-zero:- 如果业务有“pageSize=0 时返回所有数据”的需求,配置
page-size-zero: true。注意此时PageInfo的total是实际数据条数(可能很大),pages为 1。
- 如果业务有“pageSize=0 时返回所有数据”的需求,配置
- 避免与
RowBounds混用:- 如果同时使用了
PageHelper.startPage和RowBounds参数,PageHelper.startPage的优先级更高。
- 如果同时使用了
四、总结
- 核心
PageHelper:提供了 MyBatis 物理分页的核心拦截能力和基础 API (startPage,PageInfo)。 - 关键扩展
pagehelper-spring-boot-starter:极大简化了在 Spring Boot 项目中的集成和使用,是当前最推荐的使用方式,实现了开箱即用。 - 参数模式扩展:提供
startPage之外更灵活的传参方式(方法参数、RowBounds),适应不同编码风格。 PageInfo对象:作为核心功能的一部分,提供了强大的分页信息封装能力,是返回给前端的理想数据结构。
选择建议:对于 Spring Boot 项目,直接使用 pagehelper-spring-boot-starter 是最佳实践。充分利用其自动配置和属性配置,结合 PageHelper.startPage 和 PageInfo,可以非常高效、简洁地实现功能强大且信息完整的分页功能。理解核心原理和注意事项(尤其是 startPage 的调用位置和线程特性)是避免踩坑的关键。
如果你有特定的 PageHelper 扩展需求(比如集成到其他框架,或者自定义分页逻辑),可以进一步探讨其 SPI 机制或自定义拦截器实现。
