PageHelper 的用法

28 阅读3分钟

PageHelper 的用法比手写 LIMIT 简单得多。它的核心思想是“只改一行代码,不碰 SQL”:在要分页的查询之前,调用 PageHelper.startPage(pageNum, pageSize) 开启分页功能。

下面以最常用的 Spring Boot 项目为例,介绍完整的操作流程。

1. 引入依赖

在你的 pom.xml 文件中,添加 pagehelper-spring-boot-starter 依赖。

<dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper-spring-boot-starter</artifactId>
    <version>1.4.7</version> <!-- 建议使用最新稳定版本 -->
</dependency>

这个 Starter 会自动完成配置,无需额外配置拦截器。

2. 可选配置

在 application.yml 或 application.properties 中,可以做一些个性化配置:

pagehelper:
  helper-dialect: mysql       # 指定数据库类型,Starter会自动识别,但建议明确指定
  reasonable: true            # 分页合理化:pageNum<1查第一页,>总页数查最后一页
  support-methods-arguments: true # 支持通过 Mapper 接口参数传递分页参数

3. 核心用法

这是PageHelper最标准的用法,三步即可:

第一步:在查询前调用 PageHelper.startPage,设置页码和每页大小。 第二步:执行一个普通的 Mapper 查询,不包含任何分页代码。 第三步:用 PageInfo 封装查询结果,获得完整的分页信息。

完整代码示例:

import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;

@Service
public class UserService {
    @Autowired
    private UserMapper userMapper;

    public PageInfo<User> findUsersByPage(int pageNum, int pageSize) {
        // 1. 开启分页,只对紧随其后的第一个查询生效
        PageHelper.startPage(pageNum, pageSize);

        // 2. 执行普通查询,PageHelper 会自动拦截并追加 LIMIT
        List<User> userList = userMapper.selectAllUsers();

        // 3. 用 PageInfo 封装结果,它包含了所有分页信息
        PageInfo<User> pageInfo = new PageInfo<>(userList);
        return pageInfo;
    }
}

4. 返回的 PageInfo 对象里有什么?

PageInfo 对象可以直接返回给前端,它包含了前端分页组件需要的所有信息:

属性说明
total总记录数
pages总页数
pageNum当前页码
pageSize每页数量
list当前页的数据列表
hasPreviousPage是否有上一页
hasNextPage是否有下一页
isFirstPage / isLastPage是否为第一/最后一页

5. ⚠️ 重要注意事项

原理说明

PageHelper 本质上是利用 MyBatis 的拦截器机制,在执行查询前,额外执行一次 SELECT COUNT(*) 来获取总记录数,并在原 SQL 后追加 LIMIT 子句。

核心原则

  1. 紧挨查询:PageHelper.startPage() 必须紧挨着它要分页的那个 Mapper 查询方法,中间不能有其他数据库查询操作。
  2. 只对第一个查询生效:它只会影响紧随其后的第一个 SELECT 查询。
  3. 不支持 for update:分页插件不支持带有 for update 的 SQL。

使用陷阱

  1. ThreadLocal 污染:如果在一个线程中调用了 PageHelper.startPage(),但后续没有执行查询,分页参数会一直残留,可能意外影响其他查询。解决方法:可以在每次分页后调用 PageHelper.clearPage() 手动清理上下文。
  2. 勿重复分页:如果 Mapper 的 SQL 里已经写了 LIMIT,PageHelper 会再次添加,导致 SQL 语法错误或结果异常。
  3. 复杂关联查询:在复杂的多表关联查询中,如果存在 1:N 关系,PageHelper 自动生成的 COUNT 语句可能不准确。

6. 进阶用法

分页的同时进行排序

PageHelper.startPage(1, 10).orderBy("id desc");
// 或
PageHelper.startPage(1, 10, "id desc");

手动控制 COUNT 查询

// 禁用总记录数查询,用于不需要 total 的场景,可提升性能
PageHelper.startPage(1, 10, false);

// 使用 Lambda 表达式进行分页,可自定义 COUNT 逻辑
PageInfo<User> pageInfo = PageHelper.startPage(1, 10)
    .doSelectPageInfo(() -> userMapper.selectUsersWithComplexJoin());

总结

总的来说,引入依赖 → 调用 startPage → 执行查询 → 封装 PageInfo 这四步就是 PageHelper 最核心的使用流程。它把分页逻辑从 SQL 中抽离出来,让代码更干净,也更容易维护。