Spring Data 查询分页 -- 分页结果返回
针对数据查询中的分页请求参数和分页结果返回,Spring 做了建模抽象并提供了相应的工具实现,这部分模型和工具包含在包spring-data-commons中,本文对其中分页结果返回部分做一下梳理,方便在开发中使用 。
分页结果返回
接口Page
– 建模分页结果集合的一页
接口方法 | 介绍 |
---|---|
int getTotalPages() | 获取整个数据集合的总页数 |
int getTotalElements() | 获取整个数据集合的总记录数 |
<S> Page<S> map(Converter<? super T, ? extends S> converter) | 将该页数据内的每条记录使用converter做转换后构造并返回一个新的Page对象 |
接口Page
继承自接口Slice
,所以一个Page
对象除了以上能力之外,还具备一个接口Slice
所定义的能力。下面我们来看接口Slice
的定义。
接口Slice
– 建模一个记录集合中连续的若干条记录(一片数据)
接口Slice
– 建模一个记录集合中连续的若干条记录(一片数据),同时如果存在上/下一片数据的话,提供方法可获取相应的Pageable
对象。
接口方法 | 介绍 |
---|---|
int getNumber() | 将整个数据集合的分成若干片,此函数返回当前片在所有片中的索引,可以理解成分页数据的页码 |
int getSize() | 返回当前数据片中可容纳数据记录的条数,可以理解成分页数据的页面大小 |
int getNumberOfElements() | 返回当前数据片中实际包含的数据记录的条数 |
List <T> getContent() | 返回当前数据片内的数据记录集合 |
boolean hasContent() | 返回当前数据片内的是否包含数据记录 |
Sort getSort() | 返回当前数据片内的排序参数 |
boolean isFirst() | 返回当前数据片是否是整个数据集合所有分片的第一个数据片, 注意 : 第一个数据分片的索引是0 |
boolean isLast() | 返回当前数据片是否是整个数据集合所有分片的最后一个数据片 |
boolean hasPrevious() | 返回当前数据片是否已经是整个数据集合所有分片的第一个数据片 |
boolean hasNext() | 返回当前数据片是否已经是整个数据集合所有分片的最后一个数据片 |
Pageable nextPageable() | 返回当前数据片的下一片数据的分页查询对象,如果不存在下一片数据,则返回null
|
Pageable previousPageable() | 返回当前数据片的上一片数据的分页查询对象,如果不存在上一片数据,则返回null
|
<S> Slice<S> map(Converter<? super T, ? extends S> converter) | 将该片数据内的每条记录使用converter做转换后构造并返回一个新的Slice对象 |
抽象类 Chunk
– 实现了接口Slice
定义的hasNext()
之外的所有接口方法
类 Chunk
实现了接口Slice
定义的除hasNext()
之外的所有接口方法,该类用于有一个Pageable
配置限定的一部分记录,从字面上来理解,可以称作是一个"数据块"。因为Chunk
本身不包含整个数据集合中总记录数的信息,所以接口方法hasNext()
交由具体的实现类来提供。
类 Chunk
是抽象类,所以在真正构造表示分页数据的对象时并不使用类 Chunk
,而是使用其实现类PageImpl
。
实现类 PageImpl
接口Page
的基础实现
实现类 PageImpl
继承自Chunk
,这是一个完全实现了所有接口Page
/Slice
定义了的方法的实现类。在真正构造分页数据对象时,主要使用该类。
基于以下三个基本信息,可以构造一个PageImpl
对象 :
- 分页数据记录集合,
- 分页数据记录集合对应的
Pageable
对象,也就是相应的分页请求参数对象, - 整个数据集合的记录总数。
下面我们通过一张图来看PageImpl
,Page
,Slice
,Chunk
之间的关系 :
PageImpl
使用的一个例子
该代码片段摘自Spring
的工具类PageableExecutionUtils
,这是一个用于处理分页查询请求的工具类,当分页查询结果返回时,该类都是使用PageImpl
包装结果集合为一个Page
对象。
/**
* 使用 当前返回的分页查询结果集合 content, 当前分页请求参数 pageable,和 整个记录集合总记录数获取工具 构造一个
* Page 对象并返回。
*
* 这是一个静态泛型方法,这里 T 表示记录的类型。
*
* @param content 分页查询结果的记录集合,不允许为 null。
* @param pageable 分页查询请求参数,可以为 null,这个参数时发起分页请求时根据请求参数构造的。
* @param totalSupplier 用于计算整个数据集合总记录数的工具,不能为 null.
**/
public static <T> Page<T> getPage(List<T> content, Pageable pageable, TotalSupplier totalSupplier) {
Assert.notNull(content, "Content must not be null!");
Assert.notNull(totalSupplier, "TotalSupplier must not be null!");
if (pageable == null || pageable.getOffset() == 0) {
// 查询结果没有被要求分页,或者处于请求第一页的情况
if (pageable == null || pageable.getPageSize() > content.size()) {
// 1.查询结果没有被要求分页,或者
// 2.查询结果被要求分页,但是处于第一页,并且页尺寸大于实际记录数量
return new PageImpl<T>(content, pageable, content.size());
}
// 查询结果被要求分页,并且尚未获取整个数据记录集合中的总记录数
return new PageImpl<T>(content, pageable, totalSupplier.get());
}
if (content.size() != 0 && pageable.getPageSize() > content.size()) {
// 查询结果被要求分页,并且判断出当前正在查询整个数据集合的最后一页
return new PageImpl<T>(content, pageable, pageable.getOffset() + content.size());
}
// 其他情况:统一使用 当前返回的分页查询结果集合 content, 当前分页请求参数 pageable,
// 和 整个记录集合总记录数获取工具 构造一个 PageImpl对象。
return new PageImpl<T>(content, pageable, totalSupplier.get());
}