javascript
Spring Data Solr教程:分页
在我的Spring Data Solr教程的較早部分中,我們實現了一個簡單的搜索功能,該功能用于搜索待辦事項的信息。 我們搜索功能的當前實現將所有搜索結果顯示在一個頁面中。 對于大多數現實生活中的應用程序而言,這不是可行的解決方案,因為搜索結果的數量可能太大,以致搜索功能不再可用。
這篇博客文章描述了如何使用Spring Data Solr對查詢結果或搜索功能進行分頁,從而為我們提供了解決該問題的方法。
這篇博客文章分為五個部分:
- 第一部分描述了如何手動請求正確的頁面,并討論了查詢方法的不同返回類型。
- 第二部分描述了如何通過向存儲庫中添加自定義方法來獲取搜索結果計數。
- 第三部分介紹了如何對查詢方法的搜索結果進行分頁。
- 第四部分教我們如何對動態查詢的搜索結果進行分頁。
- 第五部分也是最后一部分描述了如何配置和使用一種稱為Web分頁的技術。
注意 :這些博客文章提供了其他信息,可幫助我們理解此博客文章中描述的概念:
- 使用Maven運行Solr
- Spring Data Solr教程:Solr簡介
- Spring Data Solr教程:配置
- Spring Data Solr教程CRUD(幾乎)
- Spring Data Solr教程:將自定義方法添加到單個存儲庫
- Spring Data Solr教程:動態查詢
- Spring Data Solr教程:排序
讓我們開始吧。
理論紀要
在開始對示例應用程序進行修改之前,我們將簡要介紹分頁背后的理論。 本節分為兩個小節,如下所述:
- 第一部分描述了如何指定查詢的分頁選項。
- 第二部分描述查詢方法的不同返回類型。
讓我們繼續。
指定想要的頁面
使用的分頁選項是通過使用實現Pageable接口的PageRequest類指定的。
以下是典型的分頁要求:
- 獲取屬于單個頁面的查詢結果。
- 使用單個字段的值對查詢結果進行排序時,獲取屬于單個頁面的查詢結果。
- 使用多個字段的值對查詢結果進行排序并且不同字段的排序順序相同時,獲取屬于單個頁面的查詢結果。
- 使用多個字段的值對查詢結果進行排序并且不同字段的排序順序不同時,獲取屬于單個頁面的查詢結果。
讓我們找出如何創建滿足給定要求的PageRequest對象。
首先,我們必須創建一個PageRequest對象,該對象指定我們要獲取屬于單個頁面的查詢結果。 我們可以使用以下代碼創建PageRequest對象:
//Get the query results belonging to the first page when page size is 10. new PageRequest(0, 10)其次,我們必須創建一個PageRequest對象,該對象指定當使用單個字段的值對查詢結果進行排序時,我們希望獲得屬于單個頁面的結果。 我們可以使用以下代碼創建PageRequest對象:
/Gets the query results belonging to the first page when page size is 10. //Query results are sorted in descending order by using id field. new PageRequest(0, 10 Sort.Direction.DESC, "id")第三,我們必須創建一個PageRequest對象,該對象指定當使用多個字段對查詢結果進行排序并且不同字段的排序順序相同時,我們希望獲取屬于單個頁面的結果。 我們可以使用以下代碼創建PageRequest對象:
//Gets the query results belonging to the first page when page size is 10. //Query results are sorted in descending order by using id and description fields. new PageRequest(0, 10 Sort.Direction.DESC, "id", "description")第四,我們必須創建一個PageRequest對象,該對象指定當使用多個字段對查詢結果進行排序并且不同字段的排序順序不同時,要獲取屬于單個頁面的查詢結果。 我們可以使用以下代碼創建該對象:
//Gets the query results belonging to the first page when page size is 10. //Query results are sorted in descending order order by using the description field //and in ascending order by using the id field. Sort sort = new Sort(Sort.Direction.DESC, "description").and(new Sort(Sort.Direction.ASC, "id")) new PageRequest(0, 10, sort);現在我們知道如何創建新的PageRequest對象。 讓我們繼續討論查詢方法的不同返回類型。
確定查詢方法的返回類型
當查詢方法使用分頁時,它可以具有兩種返回類型。 這些返回類型將在下面進行描述(我們將假定模型類的名稱為TodoDocument ):
- 當我們對分頁元數據感興趣時,查詢方法的返回類型必須為Page <TodoDocument> (獲取有關Page接口的更多信息,該接口聲明用于獲取分頁元數據的方法)。
- 當我們對分頁元數據不感興趣時??,查詢方法的返回類型應為List <TodoDocument> 。
獲取搜索結果計數
在開始對查詢的搜索結果進行分頁之前,我們必須實現一個函數,該函數用于獲取與給定搜索條件匹配的待辦事項條目數。 此數字是必需的,以便我們可以在前端實現分頁邏輯。
我們可以按照以下步驟實現此功能:
在以下小節中將更詳細地描述這些步驟。
向我們的存儲庫添加自定義方法
目前,如果不向存儲庫中添加自定義方法,就無法創建計數查詢。 我們可以按照以下步驟進行操作:
讓我們繼續前進,找出實現方法。
創建自定義存儲庫界面
我們可以按照以下步驟創建自定義存儲庫接口:
CustomTodoDocumentRepository接口的源代碼如下所示:
public interface CustomTodoDocumentRepository {public long count(String searchTerm);//Other methods are omitted }實施自定義存儲庫界面
我們可以按照以下步驟實現自定義存儲庫接口:
讓我們仔細看一下count()方法的實現。 我們可以通過執行以下步驟來實現此方法:
TodoDocumentRepositoryImpl類的源代碼如下所示:
import org.springframework.data.solr.core.SolrTemplate; import org.springframework.data.solr.core.query.Criteria; import org.springframework.data.solr.core.query.SimpleQuery; import org.springframework.stereotype.Repository;import javax.annotation.Resource;@Repository public class TodoDocumentRepositoryImpl implements CustomTodoDocumentRepository {@Resourceprivate SolrTemplate solrTemplate;@Overridepublic long count(String searchTerm) {String[] words = searchTerm.split(" ");Criteria conditions = createSearchConditions(words);SimpleQuery countQuery = new SimpleQuery(conditions);return solrTemplate.count(countQuery);}private Criteria createSearchConditions(String[] words) {Criteria conditions = null;for (String word: words) {if (conditions == null) {conditions = new Criteria("title").contains(word).or(new Criteria("description").contains(word));}else {conditions = conditions.or(new Criteria("title").contains(word)).or(new Criteria("description").contains(word));}}return conditions;}//Other methods are omitted. }修改實際存儲庫界面
通過擴展CustomTodoRepositoryInterface,我們可以使自定義count()方法對我們的存儲庫用戶可見。 TodoDocumentRepository的源代碼如下所示:
public interface TodoDocumentRepository extends CustomTodoRepository, SolrCrudRepository<TodoDocument, String> {//Repository methods are omitted. }使用自定義存儲庫方法
我們可以按照以下步驟使用創建的存儲庫方法:
下面將更詳細地描述這些步驟。
注意 :我們還必須進行其他更改,但是由于它們與Spring Data Solr不相關,因此在此不再贅述。
修改服務接口
我們必須通過向其添加一個新的countSearchResults()方法來修改TodoIndexService接口。 此方法將使用的搜索詞作為方法參數,并返回搜索結果計數。 TodoIndexService接口的源代碼如下所示:
public interface TodoIndexService {public long countSearchResults(String searchTerm);//Other methods are omitted. }實施修改后的接口
通過執行以下步驟,我們可以實現countSearchResults()方法:
RepositoryTodoIndexService類的相關部分如下所示:
import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional;import javax.annotation.Resource; import java.util.ArrayList; import java.util.List;@Service public class RepositoryTodoIndexService implements TodoIndexService {@Resourceprivate TodoDocumentRepository repository;@Overridepublic long countSearchResults(String searchTerm) {return repository.count(searchTerm);}//Other methods are omitted. }分頁查詢方法的查詢結果
使用查詢方法創建查詢時,可以按照以下步驟對查詢結果進行分頁:
讓我們開始吧。
修改存儲庫界面
我們可以通過向查詢方法添加Pageable參數來向我們的存儲庫添加分頁支持,該方法用于構建執行的查詢。 讓我們看一下查詢方法的聲明。
從方法名稱查詢生成
通過使用從方法名策略生成查詢來創建執行的查詢時,我們必須向TodoDocumentRepository接口的findByTitleContainsOrDescriptionContains()方法添加Pageable參數。 我們的存儲庫界面的這些源代碼如下所示:
import org.springframework.data.domain.Pageable; import org.springframework.data.solr.repository.SolrCrudRepository;import java.util.List;public interface TodoDocumentRepository extends CustomTodoDocumentRepository, SolrCrudRepository<TodoDocument, String> {public List<TodoDocument> findByTitleContainsOrDescriptionContains(String title, String description, Pageable page); }命名查詢
使用命名查詢時,我們必須在TodoDocumentRepository接口的findByNamedQuery()方法中添加Pageable參數。 TodoDocumentRepository接口的源代碼如下所示:
import org.springframework.data.domain.Pageable; import org.springframework.data.solr.repository.Query; import org.springframework.data.solr.repository.SolrCrudRepository;import java.util.List;public interface TodoDocumentRepository extends CustomTodoDocumentRepository, SolrCrudRepository<TodoDocument, String> {@Query(name = "TodoDocument.findByNamedQuery")public List<TodoDocument> findByNamedQuery(String searchTerm, Pageable page); }@Query注釋
使用@Query批注創建執行的查詢時,我們必須在TodoDocumentRepository接口的findByQueryAnnotation()方法中添加Pageable參數。 我們的存儲庫界面的源代碼如下所示:
import org.springframework.data.domain.Pageable; import org.springframework.data.solr.repository.Query; import org.springframework.data.solr.repository.SolrCrudRepository;import java.util.List;public interface TodoDocumentRepository extends CustomTodoDocumentRepository, SolrCrudRepository<TodoDocument, String> {@Query("title:*?0* OR description:*?0*")public List<TodoDocument> findByQueryAnnotation(String searchTerm, Pageable page); }修改服務層
我們必須對示例應用程序的服務層進行以下修改:
注意 :我們還必須進行其他更改,但是由于它們與Spring Data Solr不相關,因此在此不再贅述。
TodoIndexService接口的源代碼如下所示:
import org.springframework.data.domain.Pageable; import java.util.List;public interface TodoIndexService {public List<TodoDocument> search(String searchTerm, Pageable page);//Other methods are omitted. }我們可以通過對RepositoryIndexService類的search()方法進行以下更改來使用修改后的查詢方法:
讓我們來看一下search()方法的不同實現。
從方法名稱查詢生成
通過使用從方法名策略生成查詢來構建查詢時,可以使用TodoDocumentRepository接口的findByTitleContainsOrDescriptionContains()方法獲取屬于特定頁面的查詢結果。
RepositoryTodoIndexService類的相關部分如下所示:
import org.springframework.beans.factory.annotation.Value; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional;import javax.annotation.Resource; import java.util.ArrayList; import java.util.List;@Service public class RepositoryTodoIndexService implements TodoIndexService {@Resourceprivate TodoDocumentRepository repository;@Overridepublic List<TodoDocument> search(String searchTerm, Pageable page) {return repository.findByTitleContainsOrDescriptionContains(searchTerm, searchTerm, page);}//Other methods are omitted }命名查詢
當我們使用命名查詢來構建執行的查詢時,可以使用TodoDocumentRepository接口的findByNamedQuery()方法獲取屬于特定頁面的搜索結果。
RepositoryTodoIndexService類的相關部分如下所示:
import org.springframework.beans.factory.annotation.Value; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional;import javax.annotation.Resource; import java.util.ArrayList; import java.util.List;@Service public class RepositoryTodoIndexService implements TodoIndexService {@Resourceprivate TodoDocumentRepository repository;@Overridepublic List<TodoDocument> search(String searchTerm, Pageable page) {return repository.findByNamedQuery(searchTerm, page);}//Other methods are omitted }@Query注釋
使用@Query批注構建查詢時,可以通過調用TodoDocumentRepository接口的findByQueryAnnotation()方法來獲取屬于特定頁面的搜索結果。
RepositoryTodoIndexService類的源代碼如下所示:
import org.springframework.beans.factory.annotation.Value; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional;import javax.annotation.Resource; import java.util.ArrayList; import java.util.List;@Service public class RepositoryTodoIndexService implements TodoIndexService {@Resourceprivate TodoDocumentRepository repository;@Overridepublic List<TodoDocument> search(String searchTerm, Pageable page) {return repository.findByQueryAnnotation(searchTerm, page);}//Other methods are omitted }分頁動態查詢的查詢結果
我們可以按照以下步驟對動態查詢的查詢結果進行分頁:
在以下小節中將更詳細地描述這些步驟。
更改自定義存儲庫
我們必須向我們的自定義存儲庫添加分頁支持。 我們可以按照以下步驟進行操作:
讓我們繼續前進,找出實現方法。
更改自定義存儲庫界面
我們必須在CustomTodoDocumentRepository接口中聲明的search()方法中添加Pageable參數。 我們的自定義存儲庫界面的源代碼如下所示:
import org.springframework.data.domain.Pageable;import java.util.List;public interface CustomTodoDocumentRepository {public List<TodoDocument> search(String searchTerm, Pageable page);//Other methods are omitted. }實施自定義存儲庫方法
我們的下一步是向search()方法的實現中添加分頁支持。 通過執行以下步驟,我們可以實現TodoDocumentRepositoryImpl類的search()方法:
TodoDocumentRepositoryImpl類的源代碼如下所示:
import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.solr.core.SolrTemplate; import org.springframework.data.solr.core.query.Criteria; import org.springframework.data.solr.core.query.SimpleQuery; import org.springframework.stereotype.Repository;import javax.annotation.Resource; import java.util.ArrayList; import java.util.List;@Repository public class TodoDocumentRepositoryImpl implements CustomTodoDocumentRepository {@Resourceprivate SolrTemplate solrTemplate;@Overridepublic List<TodoDocument> search(String searchTerm, Pageable page) {String[] words = searchTerm.split(" ");Criteria conditions = createSearchConditions(words);SimpleQuery search = new SimpleQuery(conditions);search.setPageRequest(page);Page results = solrTemplate.queryForPage(search, TodoDocument.class);return results.getContent();}private Criteria createSearchConditions(String[] words) {Criteria conditions = null;for (String word: words) {if (conditions == null) {conditions = new Criteria("title").contains(word).or(new Criteria("description").contains(word));}else {conditions = conditions.or(new Criteria("title").contains(word)).or(new Criteria("description").contains(word));}}return conditions;}//Other methods are omitted. }使用自定義存儲庫
在使用修改后的存儲庫方法之前,我們必須對示例應用程序的服務層進行以下更改:
下面將更詳細地描述這些步驟。
注意 :我們還必須進行其他更改,但是由于它們與Spring Data Solr不相關,因此在此不再贅述。
修改服務接口
我們必須向TodoIndexService接口的search()方法添加Pageable參數。 TodoIndexService的源代碼如下所示:
import org.springframework.data.domain.Pageable; import java.util.List;public interface TodoIndexService {public List<TodoDocument> search(String searchTerm, Pageable page);//Other methods are omitted. }實施服務接口
當使用Spring Data Solr的標準API進行構建時,我們可以通過調用自定義存儲庫的search()方法并將用戶搜索詞和Pageable對象作為方法參數來獲取查詢結果。
RepositoryTodoIndexService類的源代碼如下所示:
import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; import javax.annotation.Resource; import java.util.List;@Service public class RepositoryTodoIndexService implements TodoIndexService {@Resourceprivate TodoDocumentRepository repository;@Overridepublic List<TodoDocument> search(String searchTerm, Pageable page) {return repository.search(searchTerm, page);}//Other methods are omitted. }使用網頁分頁
一個問題仍然沒有答案。 問題是:
在何處指定用于對查詢的查詢結果進行分頁的分頁選項?
我們將使用稱為Web pagination的技術來創建查詢的分頁選項。 此技術基于稱為PageableArgumentResolver的自定義參數解析器類。 此類解析來自HTTP請求的分頁信息,并使向控件方法添加Pageable方法參數成為可能。
本節描述了如何在示例應用程序中配置和使用此技術。 它分為三個小節:
- 第一部分描述了如何配置PageableArgumentResolver類。
- 第二小節介紹了如何使用它。
- 最后一個小節討論了Web分頁的利弊。
讓我們找出如何在示例應用程序中使用此技術。
組態
本小節描述了如何配置PageableArgumentResolver類,該類將用于從HTTP請求中提取分頁選項。 讓我們找出如何通過使用Java配置和XML配置來做到這一點。
Java配置
我們可以通過對ExampleApplicationContext類進行以下更改來添加自定義參數自變量解析器:
ExampleApplicationContext類的相關部分如下所示:
import org.springframework.data.web.PageableArgumentResolver; import org.springframework.web.method.support.HandlerMethodArgumentResolver; import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; import org.springframework.web.servlet.mvc.method.annotation.ServletWebArgumentResolverAdapter;import java.util.List;//Annotations are omitted. public class ExampleApplicationContext extends WebMvcConfigurerAdapter {@Overridepublic void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {PageableArgumentResolver pageableArgumentResolver = new PageableArgumentResolver();argumentResolvers.add(new ServletWebArgumentResolverAdapter(pageableArgumentResolver));}//Other methods are omitted. }XML配置
我們可以通過對exampleApplicationContext.xml文件進行以下更改來配置自定義參數解析程序:
exampleApplicationContext.xml文件的相關部分如下所示:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:mvc="http://www.springframework.org/schema/mvc"xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd"><mvc:annotation-driven><mvc:argument-resolvers><bean id="pageagleArgumentResolver" class="org.springframework.data.web.PageableArgumentResolver"/></mvc:argument-resolvers></mvc:annotation-driven><!-- Configuration is omitted. --> </beans>用法
使用前面介紹的方法之一配置PageableArgumentResolver類后,可以將Pageable方法參數添加到控制器方法中。 TodoController類的search()方法就是一個很好的例子。 其源代碼的相關部分如下所示:
import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.*;import javax.annotation.Resource; import java.util.List;@Controller public class TodoController {//Fields are omitted.@RequestMapping(value = "/api/todo/search/{searchTerm}", method = RequestMethod.GET)@ResponseBodypublic List<TodoDTO> search(@PathVariable("searchTerm") String searchTerm, Pageable page) {//Implementation is omitted.}//Other methods are omitted. }但是,將Pageable參數添加到controller方法還不夠。 我們仍然必須將分頁選項添加到HTTP請求。 這是通過在請求中添加特殊的請求參數來完成的。 這些請求參數描述如下:
- page.page request參數指定所請求的頁面。
- page.size請求參數指定頁面大小。
- page.sort請求參數指定用于對查詢結果進行排序的屬性。
- page.sort.dir請求參數指定排序順序。
讓我們花點時間思考一下Web分頁的利弊。
利弊
在決定在我們的應用程序中使用它之前,我們應該意識到Web分頁的優缺點。 讓我們找出它們是什么。
優點
使用網頁分頁有一個主要好處:
將分頁選項從Web層轉移到存儲庫層是一件容易的事。 我們要做的就是配置自定義參數解析器,將Pageable參數添加到控制器方法,并使用特定的請求參數發送分頁選項。 這比處理代碼中的分頁選項和手動創建PageRequest對象要簡單得多。
缺點
下面介紹了使用Web分頁的缺點:
- Web分頁在Web層和Spring Data之間創建依賴關系。 這意味著存儲庫層的實現細節會泄漏到應用程序的上層。 盡管純粹主義者可能會聲稱這是一個巨大的錯誤,但我不同意他們的觀點。 我認為抽象應該使我們的生活更輕松,而不是更艱難。 我們還必須記住, 泄漏抽象定律規定,所有非平凡抽象在某種程度上都是泄漏的。
- Web分頁的一個真正的缺點是,只有在使用單個字段對搜索結果進行排序的情況下,我們才能使用它。 盡管對于大多數用例來說這是完全可以的,但在某些情況下這會成為問題。 如果發生這種情況,我們必須手動處理分頁選項。
摘要
現在,我們已將分頁搜索結果添加到示例應用程序中。 本教程教會了我們以下內容:
- 我們學習了創建新的PageRequest對象。
- 我們了解到可以從兩個不同的選項中選擇查詢方法的返回類型。
- 我們學習了對查詢方法和動態查詢的查詢結果進行分頁。
- 我們知道如何使用網頁分頁,并且知道它的優缺點。
我的Spring Data Solr教程的下一部分描述了如何向所有Spring Data Solr存儲庫添加自定義方法。
PS此博客文章的示例應用程序可在Github上獲得( 查詢方法和動態查詢 )。
翻譯自: https://www.javacodegeeks.com/2013/05/spring-data-solr-tutorial-pagination.html
總結
以上是生活随笔為你收集整理的Spring Data Solr教程:分页的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 种子经营网上备案APP(种子经营网上备案
- 下一篇: Spring4有条件