全部学科
NodeJS全栈
nodejs
Python全栈
python
小程序首页
📅 2026-05-20 8 分钟 ✍️ juanwangdev

PageHelper 分页插件

PageHelper 是国内最流行的 MyBatis 物理分页插件,通过拦截 MyBatis 执行器实现 SQL 改写,支持 MySQL、Oracle、PostgreSQL、SQL Server 等多种数据库方言。其核心优势是使用简单、无需修改原有 SQL,且支持 PageInfo 封装分页结果。

依赖引入

XML
<dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper-spring-boot-starter</artifactId>
    <version>2.0.0</version>
</dependency>

非 Spring Boot 项目使用 pagehelper 核心包,并在 mybatis-config.xml 中手动配置插件。

快速使用

基本分页查询

Java
@Service
public class UserService {

    @Autowired
    private UserMapper userMapper;

    public PageInfo<User> listUsers(int pageNum, int pageSize) {
        // 开启分页(必须在查询前调用,且只对紧随其后的一次查询生效)
        PageHelper.startPage(pageNum, pageSize);

        // 执行查询(PageHelper 自动拦截并改写 SQL)
        List<User> users = userMapper.selectAll();

        // 封装为 PageInfo 对象
        return new PageInfo<>(users);
    }
}

PageInfo 分页信息

Java
PageInfo<User> pageInfo = userService.listUsers(1, 10);

System.out.println("当前页码:" + pageInfo.getPageNum());       // 1
System.out.println("每页条数:" + pageInfo.getPageSize());      // 10
System.out.println("总记录数:" + pageInfo.getTotal());         // 100
System.out.println("总页数:" + pageInfo.getPages());           // 10
System.out.println("当前页数据:" + pageInfo.getList());        // List<User>
System.out.println("是否第一页:" + pageInfo.isIsFirstPage());   // true
System.out.println("是否有上一页:" + pageInfo.isHasPreviousPage()); // false

带条件的分页查询

Java
public PageInfo<User> listByStatus(int pageNum, int pageSize, Integer status) {
    PageHelper.startPage(pageNum, pageSize);
    // 条件查询自动被 PageHelper 拦截并分页
    List<User> users = userMapper.selectByStatus(status);
    return new PageInfo<>(users);
}

配置参数

Spring Boot 配置

YAML
pagehelper:
  helper-dialect: mysql         # 数据库方言
  reasonable: true              # 分页合理化(pageNum<=0 时查第一页,pageNum>pages 时查最后一页)
  support-methods-arguments: true  # 支持通过 Mapper 接口参数传递分页
  params: count=countSql        # COUNT 查询的 SQL ID

核心参数说明

参数默认值说明
helperDialect自动检测数据库方言(mysql/oracle/postgresql/sqlserver 等)
reasonablefalse分页合理化,pageNum 超出范围时自动修正
pageSizeZerofalsepageSize=0 时查询全部(不分页)
supportMethodsArgumentsfalse支持从接口方法参数中获取分页参数
params见下文分页参数映射名(pageNum/.pageSize 等)
autoRuntimeDialectfalse自动根据数据源 URL 识别方言
closeConntrueCOUNT 查询后是否关闭连接

拦截器原理

拦截链路

Java
PageHelper.startPage(pageNum, pageSize)
    ↓ 将分页参数存入 ThreadLocal
ThreadLocal.set(PageParameter)
    ↓ 执行查询
mapper.selectXxx()
    ↓ MyBatis 执行器触发拦截
Executor.query() / StatementHandler.prepare()
    ↓ PageHelper 拦截器获取 ThreadLocal 参数
PageParameter.get()
    ↓ 生成并执行 COUNT SQL
SELECT COUNT(0) FROM ...
    ↓ 改写原始 SQL 追加 LIMIT/OFFSET
SELECT * FROM ... LIMIT 10 OFFSET 0
    ↓ 清除 ThreadLocal 参数
ThreadLocal.remove()
    ↓ 返回分页结果
PageInfo<List>

ThreadLocal 机制

PageHelper 使用 ThreadLocal 存储分页参数,确保线程安全且仅对当前线程的下一次查询生效:

YAML
// PageHelper.startPage 内部实现
public static <E> Page<E> startPage(int pageNum, int pageSize) {
    return startPage(pageNum, pageSize, true);
}

public static <E> Page<E> startPage(int pageNum, int pageSize, boolean count) {
    return startPage(pageNum, pageSize, count, null, null);
}

// 存入 ThreadLocal
protected static final ThreadLocal<Page> LOCAL_PAGE = new ThreadLocal<>();

public static void setLocalPage(Page page) {
    LOCAL_PAGE.set(page);
}

注意:startPage 必须紧挨着查询语句,中间不能有其他查询操作,否则分页参数会被后续查询误消费。

多数据库方言支持

PageHelper 内置多种数据库方言,自动根据 JDBC URL 识别或手动指定:

数据库方言值分页语法
MySQLmysqlLIMIT #{limit} OFFSET #{offset}
Oracleoracle嵌套 ROWNUMOFFSET...FETCH NEXT
PostgreSQLpostgresqlLIMIT #{limit} OFFSET #{offset}
SQL ServersqlserverOFFSET...ROWS FETCH NEXT...ROWS ONLY
DB2db2ROW_NUMBER() OVER()
H2h2LIMIT #{limit} OFFSET #{offset}
SQLitesqliteLIMIT #{limit} OFFSET #{offset}

自动识别方言

Java
pagehelper:
  auto-runtime-dialect: true  # 自动根据 JDBC URL 识别方言

手动指定方言

Java
// 代码级别指定
PageHelper.startPage(1, 10, "oracle");

COUNT 查询优化

自定义 COUNT SQL

PageHelper 默认自动生成 COUNT SQL,复杂场景(含 GROUP BY、DISTINCT、ORDER BY)可能生成不准确的 COUNT。可通过 countSql 参数自定义:

Java
// 方式一:在 Mapper 接口中定义 COUNT 方法
@Select("SELECT COUNT(0) FROM user u JOIN dept d ON u.dept_id = d.id WHERE u.status = #{status}")
Long countUsersByStatus(@Param("status") Integer status);

// 方式二:配置中指定 countSql
pagehelper:
  params: count=countSql  # 调用 xxx_COUNT 方法作为 COUNT 查询

关闭 COUNT 查询

Java
// 仅分页,不执行 COUNT(适用于只需下一页按钮的场景)
PageHelper.startPage(1, 10, false);

完整分页示例

text
@RestController
@RequestMapping("/users")
public class UserController {

    @Autowired
    private UserService userService;

    @GetMapping("/page")
    public PageInfo<User> list(
            @RequestParam(defaultValue = "1") int pageNum,
            @RequestParam(defaultValue = "10") int pageSize,
            @RequestParam(required = false) Integer status) {

        // 调用 Service 层分页
        if (status != null) {
            return userService.listByStatus(pageNum, pageSize, status);
        } else {
            return userService.listUsers(pageNum, pageSize);
        }
    }
}

// Service 层
@Service
public class UserService {

    @Autowired
    private UserMapper userMapper;

    public PageInfo<User> listUsers(int pageNum, int pageSize) {
        PageHelper.startPage(pageNum, pageSize);
        List<User> users = userMapper.selectAll();
        return new PageInfo<>(users);
    }

    public PageInfo<User> listByStatus(int pageNum, int pageSize, Integer status) {
        PageHelper.startPage(pageNum, pageSize);
        List<User> users = userMapper.selectByStatus(status);
        return new PageInfo<>(users);
    }
}

与 MyBatis-Plus 分页对比

特性PageHelperMyBatis-Plus 分页
分页参数传递PageHelper.startPage() ThreadLocalnew Page<>(pageNum, pageSize) 对象传参
SQL 改写方式拦截 Executor 改写 SQL拦截 Executor 改写 SQL
结果封装PageInfo<>(list)IPage<T> 对象
COUNT 查询自动生成或自定义自动生成为主
多数据库支持 20+ 种方言支持 10+ 种方言
侵入性低(ThreadLocal 传递)中(需使用 IPage 参数)
适用场景原生 MyBatis 项目MyBatis-Plus 项目

注意事项

  1. startPage 紧邻查询PageHelper.startPage() 与查询之间不能有其他查询,否则分页参数会被误消费
  2. ThreadLocal 清理:PageHelper 自动清理 ThreadLocal,但若使用线程池需注意参数残留
  3. 嵌套分页:同一线程内嵌套调用时需使用 PageHelper.clearPage() 清理上一页参数
  4. 合理分页:开启 reasonable=true 避免 pageNum 超出范围时报错或返回空数据
  5. 大偏移量性能LIMIT 1000000, 10 性能差,建议用 WHERE id > last_id LIMIT 10 游标分页替代

要点总结

  • PageHelper 通过拦截 MyBatis 执行器实现物理分页,自动改写 SQL 并执行 COUNT 查询
  • 使用 PageHelper.startPage(pageNum, pageSize) 开启分页,参数通过 ThreadLocal 传递
  • 支持 20+ 种数据库方言,可自动识别或手动指定
  • PageInfo 封装了完整的分页信息(总数、页码、数据列表、是否有上一页等)
  • 复杂 COUNT 场景可自定义 COUNT SQL 或关闭 COUNT 查询
  • 分页参数必须紧邻查询调用,避免被其他查询误消费

存放路径:D:\git2\jwdev\articles\MYBATIS\专家\生态工具与扩展\PageHelper 分页插件.md

📝 发现内容有误?点击此处直接编辑

← 上一篇 MyBatis-Plus 集成
下一篇 → 代码生成器定制
想查看更多题目和详细解析?
小程序提供完整的题库、模拟考试和详细解析
马上就来

长按或扫描二维码,立即体验

扫码体验小程序
马上就来
使用微信扫描二维码
立即体验完整题库