针对数据查询中的分页请求参数和分页结果返回,Spring
做了建模抽象并提供了相应的工具实现,这部分模型和工具包含在包spring-data-commons
中,本文对其中分页请求参数部分做一下梳理,方便在开发中使用 。
分页请求参数
Pageable
– 分页查询参数的建模
Pageable
是一个接口,用于建模分页请求参数,它主要包含以下三个基本信息:
pageNumber
– 当前页码,0-表示第一页pageSize
– 页面尺寸,表示每页最多包含多少记录sort
– 排序参数:指定查询中对某些列是升序还是降序排列
/**
* Abstract interface for pagination information.
* 分页信息的接口抽象,一般用于查询请求分页数据时封装分页有关的参数信息,
* 具体实现由其他类完成,比如 PageRequest
*
* @author Oliver Gierke
*/
public interface Pageable {
/**
* 返回目标查询结果页的页码,0表示第一页
*
* @return the page to be returned.
*/
int getPageNumber();
/**
* 返回目标查询结果页的页尺寸,必须大于0
*
* @return the number of items of that page
*/
int getPageSize();
/**
* 返回目标查询结果页中第一行在整个查询结果集中的索引,可以根据上面
* 提到的属性 pageNumber, pageSize 计算得出 ,计算公式如下 :
* offset = pageNumber * pageSize
*
* @return the offset to be taken
*/
int getOffset();
/**
* 返回排序参数.
*
* @return
*/
Sort getSort();
/**
* 根据当前 Pageable 对象,主要是 pageNumber, pageSize, sort 信息,构造并返回表示下一页的 Pageable 对象.
*
* @return
*/
Pageable next();
/**
* 根据当前 Pageable 对象,主要是 pageNumber, pageSize, sort 信息,构造并返回表示上一页或者第一页
* 的 Pageable 对象.
*
* @return
*/
Pageable previousOrFirst();
/**
* 根据当前 Pageable 对象,主要是 pageNumber, pageSize, sort 信息,构造并返回表示第一页
* 的 Pageable 对象.
*
* @return
*/
Pageable first();
/**
* 根据当前 Pageable 对象,主要是 pageNumber, pageSize 信息判断是否还有上一页,判断方法 :
* 1. 如果 pageNumber == 0 , 表示当前已经是第一页,所以没有上一页,因此返回 false;
* 2. 如果 pageNumber >0, 表示当前不在第一页,所以存在上一页,因此返回 true。
*
* @return
*/
boolean hasPrevious();
}
Pageable
仅仅是建模分页请求参数的接口,我们不可能直接创建其对象,而构造一个Pageable
对象的方式是通过其实现类PageRequest
,本文后面会介绍PageRequest
如何使用。
Sort
– 排序参数:指定查询中某些列的升降序排列
查询中经常涉及到对某一个列或者某几个列的排序问题,对这些列如何进行排序,Spring
通过类Sort
进行抽象。你可以把 Sort
理解成类似这样一个结构 :
[[createTime:desc],[name:asc],[birthdate:asc]]
上面例子中的每一个类似
[createTime:desc]
的对儿叫做一个Order
排序。一个Order
排序指出了针对某个属性/列做升序/降序排列。
通过这样一个结构化的参数对象,查询就可以理解请求者想对哪些列进行怎样的排序了。下面我们看一下Sort
类源代码中有关的部分:
package org.springframework.data.domain;
/**
* 查询的排序参数。必须针对一个或者多个属性(也就是列)指定其如何排序。这些属性/列不能指定为null。
* Sort 同时定义了一个缺省的列排序方式:升序 ASC 。
*/
public class Sort implements Iterable<org.springframework.data.domain.Sort.Order>, Serializable {
public static final Direction DEFAULT_DIRECTION = Direction.ASC;
// 保存各个排序列及其排序参数,一个 Order 相当于一个 [列,列排序方式] 对,例子 : [createTime, ASC]
private final List<Order> orders;
/**
* 基于一组提供的 Order 对象创建一个 Sort 对象,
* @param orders 不允许是 null,也不能一个元素也没有,也不能某些元素为null
*/
public Sort(Order... orders) {
this(Arrays.asList(orders));
}
/**
* 基于一组提供的 Order 对象创建一个 Sort 对象,
* 跟 Sort(Order... orders) 目的一样,区别在于参数传递的方式
*
* @param orders 不允许是 null,也不能一个元素也没有,也不能某些元素为null .
*/
public Sort(List<Order> orders) {
if (null == orders || orders.isEmpty()) {
throw new IllegalArgumentException(
"You have to provide at least one sort property to sort by!");
}
this.orders = orders;
}
/**
* 通过一组属性/列名称定义一个Sort对象,对所有属性/列使用Sort的默认排序方式。
* 实际上是针对每个传入的属性/列应用Sort默认排序方式创建了一个Order对象。
*
* @param properties 不能为 null,不能包含 null,也不能包含长度为0的字符串
*/
public Sort(String... properties) {
this(DEFAULT_DIRECTION, properties);
}
/**
* 通过一组属性/列名称定义一个Sort对象,对所有属性/列使用Sort的默认排序方式。
* 实际上是针对每个传入的属性/列应用Sort默认排序方式创建了一个Order对象。
*
* @param direction defaults to {@linke Sort#DEFAULT_DIRECTION} (for {@literal null} cases, too)
* @param properties 不能为 null,不能包含 null,也不能包含长度为0的字符串
*/
public Sort(Direction direction, String... properties) {
this(direction, properties == null ? new ArrayList<String>() : Arrays.asList(properties));
}
/**
* 跟 Sort(Direction direction, String... properties) 目的一样,区别在于属性/列参数传递的方式
*/
public Sort(Direction direction, List<String> properties) {
if (properties == null || properties.isEmpty()) {
throw new IllegalArgumentException(
"You have to provide at least one property to sort by!");
}
this.orders = new ArrayList<Order>(properties.size());
for (String property : properties) {
this.orders.add(new Order(direction, property));
}
}
/**
* 合并两个 Sort,这里你可以把一个 Sort 理解成 Order 的一个集合,合并两个 Sort,就是
* 合并两个 Order集合
*
* @param sort 可以为空 null.
* @return 参数 sort 为空时返回当前 Sort 对象, 否则返回当前 Sort 对象和参数 sort 对象合集的
* 另外一个 Sort 对象
*/
public Sort and(Sort sort) {
if (sort == null) {
return this;
}
ArrayList<Order> these = new ArrayList<Order>(this.orders);
for (Order order : sort) {
these.add(order);
}
return new Sort(these);
}
/**
* 获取某个指定属性/列的Order排序
*
* @param property
* @return 如果指定的属性/列没有被当前Sort对象包含,返回null,否则返回相应的 Order 对象
*/
public Order getOrderFor(String property) {
for (Order order : this) {
if (order.getProperty().equals(property)) {
return order;
}
}
return null;
}
/*
* 两个 Sort 相等判断的方法 :
* 1. 同一个对象视为相等;
* 2. 两个对象有一个不是 Sort 类型,视为不相等;
* 3. 两个 Sort类型的对象,如果所包含的 Order 集合相等,则视为两个 Sort对象相等。
*/
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof Sort)) {
return false;
}
Sort that = (Sort) obj;
return this.orders.equals(that.orders);
}
}
Sort.Order
Order
是一个在类Sort
内静态定义的一个类,因此这里简单地将其称为Sort.Order
。
Sort.Order
可以简单的被理解成一个二元组 [列,列排序方式(Direction)]
。一个Sort
对象其实是一个Order
对象的集合。
Sort.Direction
– 枚举定义某个列的排序方式:升序/降序
Direction
是在类Sort
内静态定义的一个枚举类型,因此这里简单地将其成为Sort.Direction
,这个枚举定义了某个列的排序方式是升序ASC
还是降序DESC
。Sort.Direction
主要是给Sort
用来指定某个列的排序方式的。Sort.Direction
同时提供了从字符串到枚举类型Sort.Direction
的转换,转换过程是大小写不敏感的,比如ASC
,asc
,ASc
都表示升序。
PageRequest
– Pageable
的基础实现
Pageable
接口仅仅是对分页请求参数的抽象,Spring
对Pageable
接口提供了一个基础实现类PageRequest
,当我们需要对一个查询指明如何进行分页时,通常使用类PageRequest
来创建相应的分页请求参数对象。
类PageRequest
实现了接口Pageable
所定义的语义,不同的是,PageRequest
提供了构造Pageable
对象的方法。以spring-data-commons-1.13.14
为例,PageRequest
提供了如下三个构造函数 :
-
PageRequest(int page, int size)
根据页码page
和页大小size
构造分页请求参数对象,不指定列排序 -
PageRequest(int page, int size, Direction direction, String... properties)
根据页码page
,页大小size
,由direction
,properties
定义的Sort
列排序参数构造分页请求参数对象 -
PageRequest(int page, int size, Sort sort)
根据页码page
,页大小size
,sort
列排序参数构造分页请求参数对象
如何使用 PageRequest/Pageable
?
举个例子来看如何使用PageRequest
/Pageable
:
// 这是一个Spring MVC控制类的一部分代码
// 依赖注入订单服务
@Autowired
OrderService orderService;
/**
* 一个Spring MVC控制器方法,分页查询订单
*
* @param pageNo 页码
* @param pageSize 页面大小
* @return
*/
@RequestMapping(value = "/page", method = RequestMethod.POST)
public Object pageOrders(@RequestParam(value = "pageNo") int pageNo,
@RequestParam(value = "pageSize") int pageSize) {
// 注意:这里使用客户端传入的页码和页尺寸创建了对象 PageRequest, 也是一个 Pageable 对象
Page<Order> page = orderService.pageOrders(new PageRequest(pageNo, pageSize));
return page;
}
而 OrderService.pageOrders()
服务方法定义如下:
// OrderService服务逻辑实现类的一部分代码
/**
* 分页查询订单
* @param pageable 分页参数
* @return
*/
// 注意这里参数pageable的类型使用Pageable即可,不必要使用PageRequest
public Page<Order> pageOrders(Pageable pageable) {
// 省略具体实现
// ...
}