分页是现代应用开发中必不可少的一环,尤其在面对大数据量的场景时,通过分页可以有效减少单次查询的返回数据量,提升性能和用户体验。在 Java 开发中,PageHelper 是一个非常流行的分页插件,简单易用。然而,如果对其原理和使用细节不够了解,在实际使用中容易掉坑。本文将详细介绍 PageHelper 的工作机制、使用方法、常见问题及解决方案,帮助开发者正确使用它,避免踩坑。
在使用 PageHelper 之前,需要引入相关依赖:
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.4.6</version>
</dependency>
如果使用的是 Spring Boot 项目,只需引入以上依赖即可,无需额外配置。PageHelper 会自动集成 MyBatis。
PageHelper 的核心方法是 PageHelper.startPage()
,它的作用是为当前线程开启分页上下文,并在接下来的查询中拦截 SQL,添加分页参数。
以下是典型的分页代码示例:
PageHelper.startPage(inputDto.getPageNum(), inputDto.getPageSize());
List<DevicePushConfig> configList = devicePushConfigMapper.queryDevicePushConfigList(query);
PageInfo<DevicePushConfig> pageInfo = new PageInfo<>(configList);
执行流程:
PageHelper.startPage()
开启分页上下文,并设置分页参数。devicePushConfigMapper.queryDevicePushConfigList(query)
被拦截,PageHelper 在 SQL 后自动添加 LIMIT
。PageInfo
封装结果,同时计算总记录数、分页信息等。PageHelper 利用 MyBatis 的插件机制拦截查询语句,在查询 SQL 中自动加入分页语法,如 MySQL 的 LIMIT
或 Oracle 的 ROWNUM
,并执行两次 SQL 查询:
SELECT COUNT(*) FROM ...
获取满足条件的记录总数。如果在同一线程中多次调用 PageHelper.startPage()
,而未清理上下文,后续查询可能会受到前一次分页的影响。
PageHelper.startPage(1, 10);
List<DataA> listA = mapperA.queryDataA(); // 第一次分页查询
PageHelper.startPage(2, 5);
List<DataB> listB = mapperB.queryDataB(); // 第二次分页查询
PageInfo<DataA> pageInfoA = new PageInfo<>(listA); // 结果可能被第二次分页干扰
在每次分页查询后,调用 PageHelper.clearPage()
清理上下文。
PageHelper.startPage(1, 10);
List<DataA> listA = mapperA.queryDataA();
PageInfo<DataA> pageInfoA = new PageInfo<>(listA);
PageHelper.clearPage(); // 清理上下文
PageHelper.startPage(2, 5);
List<DataB> listB = mapperB.queryDataB();
PageInfo<DataB> pageInfoB = new PageInfo<>(listB);
PageHelper.clearPage();
如果分页查询代码在条件分支中,而分支未被执行,分页上下文未被清理会干扰后续查询。
PageHelper.startPage(1, 10);
if (someCondition) {
List<Data> list = mapper.queryData(); // 查询未执行
}
// 后续查询会被干扰
PageHelper.clearPage()
清理上下文。如果分页查询返回的结果被转换为另一种类型(例如 DTO),再使用 PageInfo
封装,可能会导致分页总数信息丢失。
PageHelper.startPage(1, 10);
List<DevicePushConfig> configList = mapper.queryDevicePushConfigList(query);
// 数据转换
List<DevicePushConfigOutputDto> result = new ArrayList<>();
for (DevicePushConfig config : configList) {
DevicePushConfigOutputDto dto = new DevicePushConfigOutputDto();
BeanUtils.copyProperties(config, dto);
result.add(dto);
}
// 使用 PageInfo 封装
PageInfo<DevicePushConfigOutputDto> pageInfo = new PageInfo<>(result); // 总数信息丢失
分页封装应基于原始查询结果,然后再进行数据转换:
PageHelper.startPage(1, 10);
List<DevicePushConfig> configList = mapper.queryDevicePushConfigList(query);
PageInfo<DevicePushConfig> pageInfo = new PageInfo<>(configList);
// 数据转换
List<DevicePushConfigOutputDto> result = pageInfo.getList().stream()
.map(config -> {
DevicePushConfigOutputDto dto = new DevicePushConfigOutputDto();
BeanUtils.copyProperties(config, dto);
return dto;
})
.collect(Collectors.toList());
pageInfo.setList(result);
如果分页查询包含排序逻辑,而排序字段未建立索引,可能导致性能问题。
SELECT * FROM table_a
ORDER BY create_time
LIMIT 0, 10;
SELECT * FROM table_a
WHERE id > #{lastId}
ORDER BY id ASC
LIMIT 10;
分页逻辑已经通过数据库完成,如果开发者额外对结果进行代码级分页,可能导致结果不完整。
PageHelper.startPage(1, 10);
List<Data> dataList = mapper.queryData();
// 再次手动分页
List<Data> pagedResult = dataList.subList(0, Math.min(dataList.size(), 5));
避免重复分页,直接使用 PageInfo
提供的分页信息:
PageHelper.startPage(1, 10);
List<Data> dataList = mapper.queryData();
PageInfo<Data> pageInfo = new PageInfo<>(dataList);
PageHelper 是一个非常强大的分页工具,但其使用过程中需要注意以下细节:
PageHelper.startPage()
在查询前调用。通过正确理解和使用 PageHelper,开发者可以高效完成分页需求,同时规避潜在问题,提升系统性能与稳定性。
扫码关注腾讯云开发者
领取腾讯云代金券
Copyright © 2013 - 2025 Tencent Cloud. All Rights Reserved. 腾讯云 版权所有
深圳市腾讯计算机系统有限公司 ICP备案/许可证号:粤B2-20090059 深公网安备号 44030502008569
腾讯云计算(北京)有限责任公司 京ICP证150476号 | 京ICP备11018762号 | 京公网安备号11010802020287
Copyright © 2013 - 2025 Tencent Cloud.
All Rights Reserved. 腾讯云 版权所有