MyBatis 分页插件PageHelper

PageHelper 详解

一、PageHelper 是什么?

PageHelper 是一个 MyBatis 的物理分页插件,它通过拦截 MyBatis 的 SQL 执行过程,自动为 SQL 添加分页语句,让开发者无需手动编写分页 SQL。

主要作用:

  1. 自动分页 - 拦截查询 SQL,自动添加分页语句
  2. 简化代码 - 减少重复的分页逻辑编写
  3. 统一管理 - 提供统一的分页数据返回格式
  4. 多数据库支持 - 自动适配不同数据库的分页语法

二、快速使用示例

基础使用:

java 复制代码
// 1. 设置分页参数(第2页,每页10条)
PageHelper.startPage(2, 10);

// 2. 执行查询(会自动分页)
List<User> userList = userMapper.selectAll();

// 3. 获取分页信息
PageInfo<User> pageInfo = new PageInfo<>(userList);

// 使用分页数据
System.out.println("当前页:" + pageInfo.getPageNum());        // 2
System.out.println("每页条数:" + pageInfo.getPageSize());     // 10
System.out.println("总记录数:" + pageInfo.getTotal());        // 总数据量
System.out.println("总页数:" + pageInfo.getPages());          // 总页数
System.out.println("当前页数据:" + pageInfo.getList());       // 当前页的数据

三、完整配置指南

1. Maven 依赖

xml 复制代码
<!-- Spring Boot 项目 -->
<dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper-spring-boot-starter</artifactId>
    <version>1.4.6</version>
</dependency>

<!-- 非 Spring Boot 项目 -->
<dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper</artifactId>
    <version>5.3.2</version>
</dependency>

2. Spring Boot 配置(application.yml)

yaml 复制代码
pagehelper:
  # 数据库方言(自动检测可设为 auto)
  helper-dialect: mysql
  # 分页合理化:pageNum<=0 查询第一页,pageNum>总页数查询最后一页
  reasonable: true
  # 支持通过 Mapper 接口参数传递分页参数
  support-methods-arguments: true
  # 默认值为 false,设为 true 时,RowBounds 第一个参数 offset 当成 pageNum 使用
  offset-as-page-num: true
  # 分页参数合理化
  page-size-zero: false
  # 默认为 false,设为 true 允许 pageSize=0 查询全部结果
  params: count=countSql

四、多种使用方式

方式1:基本分页查询

java 复制代码
public PageInfo<User> findUsers(int pageNum, int pageSize) {
    // 开始分页
    PageHelper.startPage(pageNum, pageSize);
    
    // 查询数据(自动分页)
    List<User> list = userMapper.selectByExample(null);
    
    // 包装分页结果
    return new PageInfo<>(list);
}

方式2:带条件的分页查询

java 复制代码
public PageInfo<User> findUsersByCondition(UserQuery query, int pageNum, int pageSize) {
    PageHelper.startPage(pageNum, pageSize);
    
    Example example = new Example(User.class);
    if (StringUtils.isNotBlank(query.getName())) {
        example.createCriteria().andLike("name", "%" + query.getName() + "%");
    }
    
    List<User> list = userMapper.selectByExample(example);
    return new PageInfo<>(list);
}

方式3:返回 Page 对象

java 复制代码
public Page<User> findUsersPage(int pageNum, int pageSize) {
    PageHelper.startPage(pageNum, pageSize);
    
    // Page 是 ArrayList 的子类,自带分页信息
    Page<User> page = (Page<User>) userMapper.selectAll();
    
    // 可以直接获取分页信息
    System.out.println("总数:" + page.getTotal());
    return page;
}

方式4:使用 RowBounds(不推荐)

java 复制代码
// 不侵入代码的方式
public List<User> findUsersWithRowBounds(int pageNum, int pageSize) {
    RowBounds rowBounds = new RowBounds((pageNum-1)*pageSize, pageSize);
    return userMapper.selectByRowBounds(null, rowBounds);
}

五、PageInfo 常用属性

属性 说明 示例
getList() 当前页数据列表 List<T>
getTotal() 总记录数 100
getPages() 总页数 10
getPageNum() 当前页码 1
getPageSize() 每页数量 10
isFirstPage() 是否第一页 true
isLastPage() 是否最后一页 false
isHasNextPage() 是否有下一页 true
isHasPreviousPage() 是否有上一页 false
getNavigatePages() 导航页码数 8
getNavigatepageNums() 导航页码数组 1,2,3,4,5,6,7,8

六、进阶用法

1. 排序功能

java 复制代码
// 方式1:使用参数排序
PageHelper.startPage(1, 10, "create_time desc");

// 方式2:使用 OrderBy 方法
PageHelper.startPage(1, 10)
         .setOrderBy("age asc, name desc");

List<User> list = userMapper.selectAll();

2. 只统计数量不分页

java 复制代码
// 只进行 count 查询
PageHelper.startPage(1, 0);  // pageSize=0 时只查询总数
long total = ((Page<?>) userMapper.selectAll()).getTotal();

3. 分页拦截器设置参数

java 复制代码
// 可以在代码中动态设置参数
PageHelper.startPage(1, 10)
    .setCount(true)           // 是否进行 count 查询
    .setReasonable(false)     // 关闭合理化
    .setPageSizeZero(true);   // 允许 pageSize=0

七、常见问题解决方案

问题1:分页不生效

java 复制代码
// ❌ 错误写法:中间有其他查询
PageHelper.startPage(1, 10);
userMapper.countAll();  // 这个查询会被分页
List<User> list = userMapper.selectAll();  // 这个不会分页

// ✅ 正确写法:紧邻查询语句
PageHelper.startPage(1, 10);
List<User> list = userMapper.selectAll();  // 这个会分页

问题2:自定义 count 查询

xml 复制代码
<!-- 在 Mapper.xml 中定义 count 查询 -->
<select id="selectComplexCount" resultType="long">
    SELECT COUNT(*) FROM user WHERE status = 1
</select>
java 复制代码
// 使用自定义 count 查询
PageHelper.startPage(1, 10)
    .setCountId("selectComplexCount");

问题3:多表关联查询分页

java 复制代码
// 对于复杂查询,可以在 SQL 中使用 PageHelper
PageHelper.startPage(1, 10);
// 自己写 JOIN 查询的 SQL
List<UserDTO> list = userMapper.selectUserWithRole();

八、与 MyBatis-Plus 对比

特性 PageHelper MyBatis-Plus
侵入性 无侵入 需要继承 BaseMapper
使用方式 ThreadLocal 传入 Page 对象
功能范围 专注分页 全套 CRUD+分页
学习成本 中等
灵活性 中等

九、最佳实践建议

  1. 服务层封装
java 复制代码
public class PageUtils {
    public static <T> PageResult<T> toPageResult(PageInfo<T> pageInfo) {
        PageResult<T> result = new PageResult<>();
        result.setData(pageInfo.getList());
        result.setTotal(pageInfo.getTotal());
        result.setPageNum(pageInfo.getPageNum());
        result.setPageSize(pageInfo.getPageSize());
        return result;
    }
}
  1. 统一返回格式
java 复制代码
@Data
public class PageResult<T> {
    private List<T> data;
    private long total;
    private int pageNum;
    private int pageSize;
    private int pages;
}
  1. Controller 使用
java 复制代码
@GetMapping("/users")
public Result listUsers(@RequestParam(defaultValue = "1") int pageNum,
                        @RequestParam(defaultValue = "10") int pageSize) {
    PageInfo<User> pageInfo = userService.findUsers(pageNum, pageSize);
    return Result.success(PageUtils.toPageResult(pageInfo));
}

总结

PageHelper 是一个非常实用的 MyBatis 分页插件,通过简单的 PageHelper.startPage() 调用即可实现复杂的分页功能。使用时注意:

  1. 确保 startPage() 紧邻查询语句
  2. 合理配置数据库方言
  3. 根据需求选择合适的返回类型(PageInfo 或 Page)
  4. 对于复杂查询考虑自定义 count 语句

熟练掌握 PageHelper 可以极大提升开发效率和代码可维护性。

相关推荐
颜酱几秒前
02 | 搭骨架:用 LangGraph 编排 12 步工作流
前端·人工智能·后端
嘉伟桑1 小时前
Python调用电价API返回JSON示例:分时电价、现货电价和字段解析
后端
武子康1 小时前
Shippy:确定性工具、会话级 Sandbox 与 Live-Data Eval(4 类收敛 + 7 步实现方案 + 6 类评测指标)
前端·人工智能·后端
我叫黑大帅2 小时前
我为什么单一消费者的场景下,要用 Redis List 当消息队列?
redis·后端·面试
AskHarries2 小时前
文件上传系统
后端
止语Lab3 小时前
好的 DX 不等于少写代码——三种语言的摩擦力设计课
后端
吃饱了得干活3 小时前
别再手动解析 LLM 输出了!LangChain 四种结构化输出方案对比
后端·python·langchain
程序员天天困3 小时前
Arthas trace 命令怎么用?一行定位最慢那行代码
jvm·后端
Huiturn3 小时前
GPT 5.6 连续编码 10 小时,纯 Python 啃下 Word 二进制格式——doc2docx 实现拆解
后端
用户298698530143 小时前
Python 实现 Excel 与 Markdown 互转的实用指南
后端·python·excel