1. 引言
本文將探討分頁對於檢索信息的重要性,比較 Spring Data Reactive 分頁與 Spring Data 的分頁,並演示如何使用示例代碼實現分頁功能。
2. 分頁的重要性
分頁是處理返回大量資源端點時的基本概念。它通過將數據分解為較小、易於管理的“頁”來允許高效地檢索和呈現數據。
考慮一個顯示產品詳細信息的 UI 頁面,該頁面可能顯示 10 到 10,000 條記錄。如果 UI 設計為從後端檢索和顯示整個目錄,那麼它將消耗額外的後端資源,並導致用户等待時間過長。
實施分頁系統可以顯著提高用户體驗。 相對於一次性檢索所有記錄,更有效的方法是最初檢索少量記錄,並在請求時提供加載下一批記錄的選項。
使用分頁,後端可以返回包含較小子集(例如 10 條記錄)的初始響應,並使用偏移量或“下一頁”鏈接來檢索後續頁面。這種方法將檢索和顯示記錄的負載分佈到多個頁面中,從而改善整體應用程序體驗。
3. Spring Data 中的分頁與 Spring Data Reactive
Spring Data 是 Spring Framework 生態系統中的一個項目,旨在簡化和增強 Java 應用程序中的數據訪問。Spring Data 提供了一組常見的抽象和功能,通過減少樣板代碼並推廣最佳實踐,從而簡化開發流程。
如 Spring Data 分頁示例所述,PageRequest 對象,它接受 page、size 和 sort 參數,可用於配置和請求不同頁面。Spring Data 提供了 PagingAndSortingRepository,它提供了使用分頁和排序抽象檢索實體的方法。 存儲庫方法接受 Pageable 和 Sort 對象,可用於配置返回 Page 信息。 此 Page 對象包含 totalElements 和 totalPages 屬性,這些屬性在內部執行額外的查詢後被填充。 此信息可用於請求後續頁面上的信息。
相反,Spring Data Reactive 不完全支持分頁。 原因是 Spring Reactive 支持異步非阻塞,因此必須等待(或阻塞)直到返回特定頁面大小的所有數據,這效率不高。 但是,Spring Data Reactive 仍然支持 Pageable。 我們可以使用 PageRequest 對象來檢索特定數據塊,並添加一個顯式查詢以檢索記錄的總計數。
我們可以作為使用 Spring Data 檢索記錄的頁面上的記錄元數據,獲得 Flux。
4. 基本應用
This section covers the basic application setup and configuration. It provides a step-by-step guide to getting your first application running.
4.1 Setting up the Application
- Install Dependencies: Ensure all required dependencies are installed. This includes the core libraries and any necessary plugins.
- Configure the Application: Modify the configuration file to specify the database connection details, API keys, and other relevant settings.
- Run the Application: Execute the application using the command-line interface or a suitable IDE.
4.2 Running a Simple Example
Here's a basic example to demonstrate the application's functionality:
# This is a simple example to illustrate the application's core logic.
# It demonstrates how to fetch data from a source and display it.
def fetch_data(source):
# This function retrieves data from the specified source.
# You can replace this with your actual data retrieval logic.
data = "Example Data"
return data
if __name__ == "__main__":
data = fetch_data("some_source")
print(data)
4.1. Spring WebFlux 和 Spring Data Reactive 中分頁功能的實現
為了本文,我們將使用一個簡單的 Spring R2DBC 應用,通過 GET /products 暴露產品信息並支持分頁。
讓我們考慮一個簡單的 Product 模型:
@Data
@AllArgsConstructor
@NoArgsConstructor
@Table
public class Product {
@Id
@Getter
private UUID id;
@NotNull
@Size(max = 255, message = "The property 'name' must be less than or equal to 255 characters.")
private String name;
@NotNull
private double price;
}我們可以通過傳遞一個 Pageable 對象來從產品存儲庫中檢索產品列表。該對象包含配置,例如 Page 和 Size:。
@Repository
public interface ProductRepository extends ReactiveSortingRepository<Product, UUID> {
Flux<Product> findAllBy(Pageable pageable);
}此查詢的結果集以 Flux 形式返回,而不是 Page,因此需要單獨查詢記錄總數以填充 Page 響應。
讓我們添加一個控制器,其中包含一個 PageRequest 對象,該對象還會運行額外的查詢以檢索記錄的總數。這是因為我們的存儲庫不會返回 Page 信息,而是返回 Flux<Product>:
@GetMapping("/products")
public Mono<Page<Product>> findAllProducts(Pageable pageable) {
return this.productRepository.findAllBy(pageable)
.collectList()
.zipWith(this.productRepository.count())
.map(p -> new PageImpl<>(p.getT1(), pageable, p.getT2()));
}最後,我們必須將查詢結果集和最初收到的 Pageable 對象都發送到 PageImpl。 此類包含輔助方法,用於計算 Page 信息,其中包括用於獲取下一批記錄的元數據。
現在,當我們嘗試訪問端點時,我們應該收到包含產品列表以及頁碼元數據。
{
"content": [
{
"id": "cdc0c4e6-d4f6-406d-980c-b8c1f5d6d106",
"name": "product_A",
"price": 1
},
{
"id": "699bc017-33e8-4feb-aee0-813b044db9fa",
"name": "product_B",
"price": 2
},
{
"id": "8b8530dc-892b-475d-bcc0-ec46ba8767bc",
"name": "product_C",
"price": 3
},
{
"id": "7a74499f-dafc-43fa-81e0-f4988af28c3e",
"name": "product_D",
"price": 4
}
],
"pageable": {
"sort": {
"sorted": false,
"unsorted": true,
"empty": true
},
"pageNumber": 0,
"pageSize": 20,
"offset": 0,
"paged": true,
"unpaged": false
},
"last": true,
"totalElements": 4,
"totalPages": 1,
"first": true,
"numberOfElements": 4,
"size": 20,
"number": 0,
"sort": {
"sorted": false,
"unsorted": true,
"empty": true
},
"empty": false
}類似於 Spring Data,我們使用特定的查詢參數導航到不同的頁面,通過擴展 WebMvcConfigurationSupport,我們配置了默認屬性。
讓我們將默認頁面大小從 20 更改為 100,並同時將默認頁面設置為 0,通過重寫 addArgumentResolvers 方法實現:
@Configuration
public class CustomWebMvcConfigurationSupport extends WebMvcConfigurationSupport {
@Bean
public PageRequest defaultPageRequest() {
return PageRequest.of(0, 100);
}
@Override
protected void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
SortHandlerMethodArgumentResolver argumentResolver = new SortHandlerMethodArgumentResolver();
argumentResolver.setSortParameter("sort");
PageableHandlerMethodArgumentResolver resolver = new PageableHandlerMethodArgumentResolver(argumentResolver);
resolver.setFallbackPageable(defaultPageRequest());
resolver.setPageParameterName("page");
resolver.setSizeParameterName("size");
argumentResolvers.add(resolver);
}
}現在,我們可以從頁面 0 開始發出請求,最多 100 條記錄:
$ curl --location 'http://localhost:8080/products?page=0&size=50&sort=price,DESC'在未指定 page 和 size 參數的情況下,默認 Page 索引為 0,每頁 100 條記錄。但是,請求中設置 page size 為 50。
{
"content": [
....
],
"pageable": {
"sort": {
"sorted": false,
"unsorted": true,
"empty": true
},
"pageNumber": 0,
"pageSize": 50,
"offset": 0,
"paged": true,
"unpaged": false
},
"last": true,
"totalElements": 4,
"totalPages": 1,
"first": true,
"numberOfElements": 4,
"size": 50,
"number": 0,
"sort": {
"sorted": false,
"unsorted": true,
"empty": true
},
"empty": false
}5. 結論
在本文中,我們瞭解了 Spring Data Reactive 分頁的獨特之處。我們還實現了返回帶分頁功能的商品列表的端點。