Spring Data 查询分页 -- 分页请求参数

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/andy_zhang2007/article/details/83692451

针对数据查询中的分页请求参数和分页结果返回,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还是降序DESCSort.Direction主要是给Sort用来指定某个列的排序方式的。Sort.Direction同时提供了从字符串到枚举类型Sort.Direction的转换,转换过程是大小写不敏感的,比如ASC,asc,ASc都表示升序。

PageRequestPageable的基础实现

Pageable接口仅仅是对分页请求参数的抽象,SpringPageable接口提供了一个基础实现类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,页大小sizesort列排序参数构造分页请求参数对象

如何使用 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) {
        // 省略具体实现
        // ...
    }

相关文章

Spring Data 查询分页 – 分页结果返回

猜你喜欢

转载自blog.csdn.net/andy_zhang2007/article/details/83692451