1. 概述
本小結(jié)主要區(qū)分分頁的基本概念, 如果已經(jīng)熟悉分頁的原理的同學可以忽略本小結(jié)
2. 分頁原理
本例中, 我們將使用 offset + page 的方式進行分頁, 那么如何計算page和offset之間的關(guān)系呢? 假設(shè)我們有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 |
+--------+--------------+------------+--------+---------+
在我們梳理各種關(guān)系前, 我們先確認一些概念
按page 的方式進行分頁
比如上面的例子, 我們有11條記錄, 如果我們假設(shè)pagesize為5, page0 為起始頁, 那么page就等于3, 分別是page0, page1, page2
page: 頁數(shù), 第幾頁
pagesize: 每一頁記錄的條目數(shù)按sql查詢條件的方式進行分頁
還是上面的例子, 假設(shè)offset為5, limit 為5 如果我們執(zhí)行如下查詢
select * from emp_talbe offset 5 limit 5
我們將得到 6 | Huutte -> 10 | Blank 的五條記錄. 那么可以看出
offset: 游標, 查詢的起始點
limit: 限定查詢記錄個數(shù)
那么此時我們是希望通過使用 offset + limit 的sql 來進行分頁查詢, 那么假設(shè)我們的希望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
所以自然, 我們可以得出以下我們可能需要用到值的關(guān)系
- 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 接口的實現(xiàn)類 PageRequest
一個比較常用的實現(xiàn)類就是 PageRequest, 那么它實現(xiàn)了很多我們需要的方法, 同時提供of 靜態(tài)方法供我們調(diào)用創(chuàng)建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)
...
}
//調(diào)用
MyRepository.getEmployeebyGender("man", PageRequest.of(8, 10) )
此時, 我們已經(jīng)可以使用現(xiàn)有的實現(xiàn)類滿足我們的需求, 但是我們需要使用使用 offset 進行查詢, 那么接下來我們將要討論如何結(jié)合自己的情況來調(diào)整開發(fā)