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 等) |
| reasonable | false | 分页合理化,pageNum 超出范围时自动修正 |
| pageSizeZero | false | pageSize=0 时查询全部(不分页) |
| supportMethodsArguments | false | 支持从接口方法参数中获取分页参数 |
| params | 见下文 | 分页参数映射名(pageNum/.pageSize 等) |
| autoRuntimeDialect | false | 自动根据数据源 URL 识别方言 |
| closeConn | true | COUNT 查询后是否关闭连接 |
拦截器原理
拦截链路
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 识别或手动指定:
| 数据库 | 方言值 | 分页语法 |
|---|---|---|
| MySQL | mysql | LIMIT #{limit} OFFSET #{offset} |
| Oracle | oracle | 嵌套 ROWNUM 或 OFFSET...FETCH NEXT |
| PostgreSQL | postgresql | LIMIT #{limit} OFFSET #{offset} |
| SQL Server | sqlserver | OFFSET...ROWS FETCH NEXT...ROWS ONLY |
| DB2 | db2 | ROW_NUMBER() OVER() |
| H2 | h2 | LIMIT #{limit} OFFSET #{offset} |
| SQLite | sqlite | LIMIT #{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 分页对比
| 特性 | PageHelper | MyBatis-Plus 分页 |
|---|---|---|
| 分页参数传递 | PageHelper.startPage() ThreadLocal | new Page<>(pageNum, pageSize) 对象传参 |
| SQL 改写方式 | 拦截 Executor 改写 SQL | 拦截 Executor 改写 SQL |
| 结果封装 | PageInfo<>(list) | IPage<T> 对象 |
| COUNT 查询 | 自动生成或自定义 | 自动生成为主 |
| 多数据库 | 支持 20+ 种方言 | 支持 10+ 种方言 |
| 侵入性 | 低(ThreadLocal 传递) | 中(需使用 IPage 参数) |
| 适用场景 | 原生 MyBatis 项目 | MyBatis-Plus 项目 |
注意事项
- startPage 紧邻查询:
PageHelper.startPage()与查询之间不能有其他查询,否则分页参数会被误消费- ThreadLocal 清理:PageHelper 自动清理 ThreadLocal,但若使用线程池需注意参数残留
- 嵌套分页:同一线程内嵌套调用时需使用
PageHelper.clearPage()清理上一页参数- 合理分页:开启
reasonable=true避免 pageNum 超出范围时报错或返回空数据- 大偏移量性能:
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
📝 发现内容有误?点击此处直接编辑