Spring Data JPA教程, 第七部分: Pagination(未翻译)
The previous part of my Spring Data JPA tutorialdescribed how you can sort query results with Spring Data JPA. This blog entry will describe how you can paginate the query results by using Spring Data JPA. In order to demonstrate the pagination support of Spring Data JPA, I will add two new requirements for my example application:
- The person search results should be paginated.
- The maximum number of persons shown on a single search result page is five.
I will explain the pagination support of Spring Data JPA and my example implementation in following Sections.
Pagination with Spring Data JPA
The key of component of the Spring Data JPA pagination support is the Pageable interface which is implemented by PageRequest class. You can get a specific page of your query results by following these steps:
- Create an instance of the PageRequest class.
- Pass the created object as a parameter to the correct repository method.
The available repository methods are described in following:
- If you want to simply paginate all objects found from the database, you should use the public Page<T> findAll(Pageable pageable) method of the JpaRepository<T> interface.
- If you are building your queries by using the query generation from method name strategy, you can paginate the query results by passing the Pageable instance as a parameter of your method. Check the Spring Data JPA reference manual for more details (1.3.2.3 Special Parameter Handling).
- If you are using the JPA criteria API for building your query, you should use the public Page<T> findAll(Specification specification, Pageable pageable) method of theJpaSpecificationExecutor<T> interface.
- If you are building your queries with Querydsl, you should use the public Page<T> findAll(Predicate predicate, Pageable pageable) method of the QueryDslPredicateExercutor<T>interface.
After you have obtained the requested page, you can get a list of entities by calling thegetContent() method of the Page<T> interface.
Enough with the theory, lets take a look how the given requirements can be implemented with JPA criteria API.
Adding Pagination to Person Search Results
The given requirements can be implemented with JPA criteria API by following these steps:
- Obtain the wanted page number.
- Create the needed Specification instance.
- Create the instance of a PageRequest class.
- Pass the created Specification and PageRequest objects to the person repository.
First, I modified the search() method of the PersonService interface. In order to obtain the wanted page from the database, the repository needs to know what page it should be looking for. Thus, I had to add a new parameter to the search() method. The name of this parameter ispageIndex and it specifies the index of the wanted page. The declaration of the new search()methods is given in following:
/**
* Searches persons for a given page by using the given search term.
* @param searchTerm
* @param pageIndex
* @return A list of persons whose last name begins with the given search term and who are belonging to the given page.
* If no persons is found, this method returns an empty list. This search is case insensitive.
*/
public List<Person> search(String searchTerm, int pageIndex);
}
Second, since I am using the JPA criteria API for building the actual query, theRepositoryPersonService will obtain the needed specification by calling the static lastNameIsLike()method of PersonSpecifications class. The source code of the PersonSpecifications class is given in following:
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
public class PersonSpecifications {
public static Specification<Person> lastNameIsLike(final String searchTerm) {
return new Specification<Person>() {
@Override
public Predicate toPredicate(Root<Person> personRoot, CriteriaQuery<?> query, CriteriaBuilder cb) {
String likePattern = getLikePattern(searchTerm);
return cb.like(cb.lower(personRoot.<String>get(Person_.lastName)), likePattern);
}
private String getLikePattern(final String searchTerm) {
StringBuilder pattern = new StringBuilder();
pattern.append(searchTerm.toLowerCase());
pattern.append("%");
return pattern.toString();
}
};
}
}
Third, I need to create an instance of a PageRequest class and pass this instance to thePersonRepository. I created a private method called constructPageSpecification() to theRepositoryPersonService. This method simply creates a new instance of the PageRequest object and returns the created instance. The search method obtains the PageRequest instance by calling the constructPageSpecification() method.
The last step is to pass the created objects forward to the PersonRepository. The source code of the relevant parts of the RepositoryPersonService is given in following:
import org.slf4j.LoggerFactory;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
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;
@Service
public class RepositoryPersonService implements PersonService {
private static final Logger LOGGER = LoggerFactory.getLogger(RepositoryPersonService.class);
protected static final int NUMBER_OF_PERSONS_PER_PAGE = 5;
@Resource
private PersonRepository personRepository;
@Transactional(readOnly = true)
@Override
public List<Person> search(String searchTerm, int pageIndex) {
LOGGER.debug("Searching persons with search term: " + searchTerm);
//Passes the specification created by PersonSpecifications class and the page specification to the repository.
Page requestedPage = personRepository.findAll(lastNameIsLike(searchTerm), constructPageSpecification(pageIndex));
return requestedPage.getContent();
}
/**
* Returns a new object which specifies the the wanted result page.
* @param pageIndex The index of the wanted result page
* @return
*/
private Pageable constructPageSpecification(int pageIndex) {
Pageable pageSpecification = new PageRequest(pageIndex, NUMBER_OF_PERSONS_PER_PAGE, sortByLastNameAsc());
return pageSpecification;
}
/**
* Returns a Sort object which sorts persons in ascending order by using the last name.
* @return
*/
private Sort sortByLastNameAsc() {
return new Sort(Sort.Direction.ASC, "lastName");
}
}
I also had to modify the unit tests of the RepositoryPersonService class. The source code of the modified unit test is given in following:
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import static junit.framework.Assert.assertEquals;
import static org.mockito.Mockito.*;
public class RepositoryPersonServiceTest {
private static final long PERSON_COUNT = 4;
private static final int PAGE_INDEX = 1;
private static final Long PERSON_ID = Long.valueOf(5);
private static final String FIRST_NAME = "Foo";
private static final String FIRST_NAME_UPDATED = "FooUpdated";
private static final String LAST_NAME = "Bar";
private static final String LAST_NAME_UPDATED = "BarUpdated";
private static final String SEARCH_TERM = "foo";
private RepositoryPersonService personService;
private PersonRepository personRepositoryMock;
@Before
public void setUp() {
personService = new RepositoryPersonService();
personRepositoryMock = mock(PersonRepository.class);
personService.setPersonRepository(personRepositoryMock);
}
@Test
public void search() {
List<Person> expected = new ArrayList<Person>();
Page expectedPage = new PageImpl(expected);
when(personRepositoryMock.findAll(any(Specification.class), any(Pageable.class))).thenReturn(expectedPage);
List<Person> actual = personService.search(SEARCH_TERM, PAGE_INDEX);
ArgumentCaptor<Pageable> pageArgument = ArgumentCaptor.forClass(Pageable.class);
verify(personRepositoryMock, times(1)).findAll(any(Specification.class), pageArgument.capture());
verifyNoMoreInteractions(personRepositoryMock);
Pageable pageSpecification = pageArgument.getValue();
assertEquals(PAGE_INDEX, pageSpecification.getPageNumber());
assertEquals(RepositoryPersonService.NUMBER_OF_PERSONS_PER_PAGE, pageSpecification.getPageSize());
assertEquals(Sort.Direction.ASC, pageSpecification.getSort().getOrderFor("lastName").getDirection());
assertEquals(expected, actual);
}
}
I have now described the parts of the source code which are using Spring Data JPA for implementing the new requirements. However, my example application has a lot of web application specific “plumbing” code in it. I recommend that you take a look of the source codebecause it can help you to get a better view of the big picture.
What is Next?
I have now demonstrated to you how you can paginate your query results with Spring Data JPA. If you are interested of seeing my example application in action, you can get it from Github. The next part of my Spring Data JPA tutorial will describe how you can add custom functionality to your repository.
Spring Data JPA教程, 第七部分: Pagination(未翻译)的更多相关文章
- Spring Data JPA教程,第一部分: Configuration(翻译)
Spring Data JPA项目旨在简化基于仓库的JPA的创建并减少与数据库交互的所需的代码量.本人在自己的工作和个人爱好项目中已经使用一段时间,它却是是事情如此简单和清洗,现在是时候与你分享我的知 ...
- Spring Data JPA Tutorial Part Nine: Conclusions(未翻译)
This is the ninth and the last part of my Spring Data JPA tutorial. Now it is time to take a look of ...
- Spring Data JPA 教程(翻译)
写那些数据挖掘之类的博文 写的比较累了,现在翻译一下关于spring data jpa的文章,觉得轻松多了. 翻译正文: 你有木有注意到,使用Java持久化的API的数据访问代码包含了很多不必要的模式 ...
- Spring Data JPA教程, 第三部分: Custom Queries with Query Methods(翻译)
在本人的Spring Data JPA教程的第二部分描述了如何用Spring Data JPA创建一个简单的CRUD应用,本博文将描述如何在Spring Data JPA中使用query方法创建自定义 ...
- Spring Data JPA教程, 第二部分: CRUD(翻译)
我的Spring Data Jpa教程的第一部分描述了,如何配置Spring Data JPA,本博文进一步描述怎样使用Spring Data JPA创建一个简单的CRUD应用.该应用要求如下: pe ...
- Spring Data JPA教程, 第八部分:Adding Functionality to a Repository (未翻译)
The previous part of my tutorial described how you can paginate query results with Spring Data JPA. ...
- Spring Data JPA教程, 第六部分: Sorting(未翻译)
The fifth part of my Spring Data JPA tutorialdescribed how you can create advanced queries with Spri ...
- Spring Data JPA教程, 第五部分: Querydsl(未翻译)
The fourth part of my Spring Data JPA tutorialdescribed how you can implement more advanced queries ...
- Spring Data JPA教程, 第四部分: JPA Criteria Queries(未翻译)
The third part of my Spring Data JPA tutorialdescribed how you can create custom queries by using qu ...
随机推荐
- JDBC 事务控制
一.简介: 前面一遍提到了jdbc事务相关的概念.从中了解到事务应具有ACID特性.所以对于javaweb开发来说,某一个service层的方法,应该是一个事务,应该是具有原子性的.特别是当一个ser ...
- 服务--web服务
.面向对象和面向组件 .什么是Web服务 Web Service "Stack" .Web服务的应用分类 Web服务都是对象/组件技术在Internet中的延伸 面向对象和面向组件 ...
- UVa 11754 (中国剩余定理 枚举) Code Feat
如果直接枚举的话,枚举量为k1 * k2 *...* kc 根据枚举量的不同,有两种解法. 枚举量不是太大的话,比如不超过1e4,可以枚举每个集合中的余数Yi,然后用中国剩余定理求解.解的个数不够S个 ...
- 淘宝技术发展(Java时代:脱胎换骨)
我的师父黄裳@岳旭强曾经说过,“好的架构图充满美感”,一个架构好不好,从审美的角度就能看得出来.后来我看了很多系统的架构,发现这个言论基本成立.那么反观淘宝前面的两个版本的架构,你看哪个比较美? 显然 ...
- acdream 1408 "Money, Money, Money" (水)
题意:给出一个自然数x,要求找两个自然数a和b,任意多个a和b的组合都不能等于x,且要可以组合成大于x的任何自然数.如果找不到就输出两个0.(输出任意一个满足条件的答案) 思路:x=偶数时,无法构成, ...
- LeetCode: MergekSortedLists
Title: Merge k sorted linked lists and return it as one sorted list. Analyze and describe its comple ...
- ffmpeg 从内存中读取数据(或将数据输出到内存)
更新记录(2014.7.24): 1.为了使本文更通俗易懂,更新了部分内容,将例子改为从内存中打开. 2.增加了将数据输出到内存的方法. 从内存中读取数据 ffmpeg一般情况下支持打开一个本地文件, ...
- POJ 1064 Cable master
Cable master Time Limit: 1000MS Memory Limit: 10000K Total Submissions: 37865 Accepted: 8051 Des ...
- HDU 2056 Rectangles
Rectangles Time Limit: 1000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total ...
- C# delegate 学习 (练这么久终于悟出来点东东了,继续加油! ^_^)
前言 从事开发工作两年有余了,但还是对Delegate,Event神马的看见就头疼,文章看过无数,自己也练习过好多遍,但到用的时候或者人家换了一种形式之后就又不懂了,哎~智商捉急啊!! 但是,这两天的 ...