PageHelper
和 Mybatis-Plus
分页,两者都是基于 MyBatis
的拦截器机制来实现分页功能,通过拦截SQL语句并对其进行修改以实现分页。
PageHelper分页插件
在 MyBatis 中,当使用 PageHelper.startPage()
启动分页后,紧跟着的查询方法返回的 List
会被自动封装成一个 Page
对象。
使用
业务层直接调用 PageHelper.startPage(int pageNum, int pageSize)
传入分页参数(当前页码, 分页大小),PageHelper.startPage()
之后紧跟查询方法即可。
PageHelper
的底层机制
基于 ThreadLocal
的分页参数传递
- 调用
startPage()
时,分页参数(页码、每页条数等)会被存入当前线程的ThreadLocal
变量中。 - 后续的 MyBatis 查询方法会被
PageInterceptor
(分页拦截器)拦截,从ThreadLocal
中读取分页参数。
拦截器修改 SQL 并封装结果
- 修改 SQL :拦截器根据分页参数,动态修改原始 SQL,添加数据库方言对应的分页语句(如 MySQL 的
LIMIT
)。 - 执行
COUNT
查询 :自动生成并执行SELECT COUNT(*)
语句,获取总记录数。 - 执行
当前页
查询:执行修改后的 SQL,获取当前页的数据。 - 封装
Page
对象 :将分页数据和总记录数封装到Page
对象中,最终返回给调用方。 - 清理ThreadLocal :分页完成后,PageHelper会自动清除ThreadLocal中的分页参数,避免影响后续无关的查询。
Tips:
拦截器会将分页查询写回
startPage()
返回的Page
对象中,即就是PageHelper.startPage()
启动分页后,紧跟着的查询方法返回的List
对象和startPage()
返回的Page
对象是同一个对象。见下图验证。
Page
和 PageInfo
对象
Page
对象
-
本质 :
Page
是ArrayList
的子类,直接承载当前分页的数据集合(如当前页的查询结果列表)。 -
功能:
- 存储当前页的数据(继承自
List
)。 - 包含基本的分页元数据,如:
pageNum
:当前页码(从1开始)。pageSize
:每页显示条数。total
:总记录数(自动通过COUNT
查询获取)。pages
:总页数(由total
和pageSize
计算得出)。
- 存储当前页的数据(继承自
-
特点:
- 直接关联查询结果 :分页查询后的
List
实际上是Page
对象,可以直接强制转换获取分页元数据。 - 轻量级:仅包含当前页数据和基础分页信息。
- 直接关联查询结果 :分页查询后的
PageInfo
对象
-
本质 :
PageInfo
是对Page
的增强封装,提供更丰富的分页元数据和工具方法。 -
功能:
- 包含
Page
的所有分页元数据(如pageNum
、pageSize
、total
等)。 - 扩展了更多分页导航相关的属性,如:
isFirstPage
/isLastPage
:是否第一页/最后一页。hasPreviousPage
/hasNextPage
:是否有上一页/下一页。navigatePages
:导航页码数量(如显示前5页、后5页)。navigatepageNums
:导航页码数组(用于生成页码按钮)。prePage
/nextPage
:上一页/下一页的页码。
- 包含
-
特点:
- 脱离数据集合 :
PageInfo
本身不直接存储数据集合,而是通过构造函数接收List
(通常是Page
对象)来解析元数据。 - 面向展示层 :更适合前端分页展示,直接提供导航所需的全部信息。
- 脱离数据集合 :
PageInfo
可以通过任何List
构造,是因为Page对象继承自ArrayList,但实际只有分页后的List
(实际是Page
对象)才能正确解析分页元数据。
Mybatis-Plus分页
MyBatis-Plus 的分页功能基于拦截器实现,需手动配置分页插件。
使用
- 创建
Page
对象:封装分页参数(当前页、每页条数)。 - 执行分页查询 :调用MP提供的分页查询方法,传入
Page
和查询条件。 - 获取分页结果 :返回的
Page
对象包含数据和分页信息。
Java
public List<DeviceData> selectDeviceDataList(DeviceDataPageReqDto deviceData){
Page<DeviceData> deviceDataPage = new Page<>(deviceData.getPageNum(), deviceData.getPageSize());
this.lambdaQuery()
.eq(ObjUtil.isNotNull(deviceData.getDeviceId()), DeviceData::getIotId, deviceData.getDeviceId())
.eq(ObjUtil.isNotNull(deviceData.getFunctionId()), DeviceData::getFunctionId, deviceData.getFunctionId())
.between(ObjUtil.isNotNull(deviceData.getStartTime()) && ObjUtil.isNotNull(deviceData.getEndTime()),
DeviceData::getAlarmTime, deviceData.getStartTime(), deviceData.getEndTime())
// 执行分页查询
.page(deviceDataPage);
List<DeviceData> records = deviceDataPage.getRecords();
long total = deviceDataPage.getTotal();
// mapper层提供的分页查询方法示例
// Page<DeviceData> deviceDataPage1 = deviceDataMapper.selectPage(deviceDataPage, new LambdaQueryWrapper<>());
return records;
}
Tips:
只有在调用
MyBatis-Plus
的提供的分页方法方法时,分页插件才会生效,自动拦截和处理分页逻辑。