Spring Webflux

Spring Webflux + r2dbc 分页查询 示例1

2020-06-07  本文已影响0人  这个世界是虚拟的

1. 概述

本小结主要区分分页的基本概念, 如果已经熟悉分页的原理的同学可以忽略本小结

2. 分页原理

本例中, 我们将使用 offset + page 的方式进行分页, 那么如何计算page和offset之间的关系呢? 假设我们有11条记录, 比如emp_talbe:

+--------+--------------+------------+--------+---------+
| id | name     | hire_date  | salary | dept_id |
+--------+--------------+------------+--------+---------+
|      1 |  Hunt       | 2001-05-01 |   5000 |       4   |                    // page 0 , offset = 1
|      2 | Tony        | 2002-07-15 |   6500 |       1   |
|      3 | Sarah      | 2005-10-18 |   8000 |       5   |
|      4 | Rick         | 2007-01-03 |   7200 |       3   |
|      5 | Martin      | 2008-06-24 |   5600 |    NULL |
|      6 |  Huntte     | 2001-05-01 |   5000 |       4   |
|      7 | Montana   | 2002-07-15 |   6500 |       1   |
|      8 | Connor     | 2005-10-18 |   8000 |       5   |
|      9 | Deckard   | 2007-01-03 |   7200 |       3   |
|      10 | Blank     | 2008-06-24 |   5600 |    NULL |
|      11 | Malank   | 2008-06-24 |   5600 |    NULL |
+--------+--------------+------------+--------+---------+

在我们梳理各种关系前, 我们先确认一些概念

select * from emp_talbe offset 5 limit 5 

我们将得到 6 | Huutte -> 10 | Blank 的五条记录. 那么可以看出
offset: 游标, 查询的起始点
limit: 限定查询记录个数
那么此时我们是希望通过使用 offset + limit 的sql 来进行分页查询, 那么假设我们的希望pagesize 为5, 那么可以简单理解为我们的limit也就是5. 那么如果我们希望查询第0页的记录page0, 那么 offset = 0, limit=5, 查询第1页的记录page1, 那么 offset = 5, limit=5

// Page 0 :  select * from emp_talbe offset 0 limit 5 
// Page 1 :  select * from emp_talbe offset 5 limit 5 
// Page 2 :  select * from emp_talbe offset 10 limit 5 
所以自然, 我们可以得出以下我们可能需要用到值的关系
- limit = pagesize
- pageNumber = offset/limit 取整
- offset = pageNumber * limit
- nextPage number = offset + limit
- hasPrevious = ( offset - limit >= 0 )
- previousPageNumber = hasPrevious ? offset-limit : 0 

2. Spring R2dbc 对分页的支持

通过Spring R2dbc的官方文档: https://docs.spring.io/spring-data/r2dbc/docs/1.1.0.RELEASE/reference/html/#r2dbc.core
我们可以了解到, 我们可以使用 Pageable 对象进行分页查询. 例如:

Flux<Person> findByFirstnameOrderByLastname(String firstname, Pageable pageable);

那么接下来我们可以尝试使用这样的方式进行分页查询.

3. Pageable 接口

查看源码, 可以看出此接口涵盖了我们基本所需的分页常见方法, 这些方法也在上一步中,我们分析出了我们需要使用的值:

public interface Pageable {

    /**
     * Returns a {@link Pageable} instance representing no pagination setup.
     *
     * @return
     */
    static Pageable unpaged() {
        return Unpaged.INSTANCE;
    }

    /**
     * Returns whether the current {@link Pageable} contains pagination information.
     *
     * @return
     */
    default boolean isPaged() {
        return true;
    }

    /**
     * Returns whether the current {@link Pageable} does not contain pagination information.
     *
     * @return
     */
    default boolean isUnpaged() {
        return !isPaged();
    }

    /**
     * Returns the page to be returned.
     *
     * @return the page to be returned.
     */
    int getPageNumber();

    /**
     * Returns the number of items to be returned.
     *
     * @return the number of items of that page
     */
    int getPageSize();

    /**
     * Returns the offset to be taken according to the underlying page and page size.
     *
     * @return the offset to be taken
     */
    long getOffset();

    /**
     * Returns the sorting parameters.
     *
     * @return
     */
    Sort getSort();

    /**
     * Returns the current {@link Sort} or the given one if the current one is unsorted.
     *
     * @param sort must not be {@literal null}.
     * @return
     */
    default Sort getSortOr(Sort sort) {

        Assert.notNull(sort, "Fallback Sort must not be null!");

        return getSort().isSorted() ? getSort() : sort;
    }

    /**
     * Returns the {@link Pageable} requesting the next {@link Page}.
     *
     * @return
     */
    Pageable next();

    /**
     * Returns the previous {@link Pageable} or the first {@link Pageable} if the current one already is the first one.
     *
     * @return
     */
    Pageable previousOrFirst();

    /**
     * Returns the {@link Pageable} requesting the first page.
     *
     * @return
     */
    Pageable first();

    /**
     * Returns whether there's a previous {@link Pageable} we can access from the current one. Will return
     * {@literal false} in case the current {@link Pageable} already refers to the first page.
     *
     * @return
     */
    boolean hasPrevious();

    /**
     * Returns an {@link Optional} so that it can easily be mapped on.
     *
     * @return
     */
    default Optional<Pageable> toOptional() {
        return isUnpaged() ? Optional.empty() : Optional.of(this);
    }
}

3. Pageable 接口的实现类 PageRequest

一个比较常用的实现类就是 PageRequest, 那么它实现了很多我们需要的方法, 同时提供of 静态方法供我们调用创建PageRequest, 例如

/**
     * Creates a new unsorted {@link PageRequest}.
     *
     * @param page zero-based page index, must not be negative.
     * @param size the size of the page to be returned, must be greater than 0.
     * @since 2.0
     */
    public static PageRequest of(int page, int size) {
        return of(page, size, Sort.unsorted());
    }

    /**
     * Creates a new {@link PageRequest} with sort parameters applied.
     *
     * @param page zero-based page index.
     * @param size the size of the page to be returned.
     * @param sort must not be {@literal null}, use {@link Sort#unsorted()} instead.
     * @since 2.0
     */
    public static PageRequest of(int page, int size, Sort sort) {
        return new PageRequest(page, size, sort);
    }
/**
     * Creates a new {@link PageRequest} with sort direction and properties applied.
     *
     * @param page zero-based page index, must not be negative.
     * @param size the size of the page to be returned, must be greater than 0.
     * @param direction must not be {@literal null}.
     * @param properties must not be {@literal null}.
     * @since 2.0
     */
    public static PageRequest of(int page, int size, Direction direction, String... properties) {
        return of(page, size, Sort.by(direction, properties));
    }

那么我们比如要查询第8页, 限定每页10个记录, 我们只需使用

PageRequest.of(8, 10);

使用在Repository 的方法中:

MyRepository extends ReactiveCrudRepository<Employee, UUID>{
...
 Flux<Employee> getEmployeebyGender((@Param("gender") String gender, Pageable pageable)
...
}

//调用
MyRepository.getEmployeebyGender("man", PageRequest.of(8, 10) )

此时, 我们已经可以使用现有的实现类满足我们的需求, 但是我们需要使用使用 offset 进行查询, 那么接下来我们将要讨论如何结合自己的情况来调整开发

上一篇下一篇

猜你喜欢

热点阅读