Spring Data JPA and pagination

Let us start with the classic JPA way to support pagination. Consider a simple domain class – A ‘Member’ with attributes first name, last name. To support pagination on a list of members, the JPA way is to support a finder which takes in the offset of the first result(firstResult) and the size of the result(maxResults) to retrieve, this way:
 
 
 
 
 
 
 

import java.util.List;

import javax.persistence.TypedQuery;

import org.springframework.stereotype.Repository;

import mvcsample.domain.Member;

@Repository
public class JpaMemberDao extends JpaDao<Long, Member> implements MemberDao{

 public JpaMemberDao(){
  super(Member.class);
 }
 @Override
 public List<Member> findAll(int firstResult, int maxResults) {
  TypedQuery<Member> query = this.entityManager.createQuery('select m from Member m', Member.class);
  return query.setFirstResult(firstResult).setMaxResults(maxResults).getResultList();
 }

 @Override
 public Long countMembers() {
  TypedQuery<Long> query = this.entityManager.createQuery('select count(m) from Member m', Long.class);
  return query.getSingleResult();
 }
}

An additional API which returns the count of the records is needed to determine the number of pages for the list of entity, as shown above. Given this API, two parameters are typically required from the UI:

  • the current page being displayed (say ‘page.page’)
  • the size of list per page (say ‘page.size’)

The controller will be responsible for transforming these inputs to the one required by the JPA – firstResult and maxResults this way:

@RequestMapping(produces='text/html')
public String list(@RequestParam(defaultValue='1', value='page.page', required=false) Integer page, 
   @RequestParam(defaultValue='10', value='page.size', required=false) Integer size, Model model){
 int firstResult = (page==null)?0:(page-1) * size;
 model.addAttribute('members',this.memberDao.findAll(firstResult, size));
 float nrOfPages = (float)this.memberDao.countMembers()/size;
 int maxPages = (int)( ((nrOfPages>(int)nrOfPages) || nrOfPages==0.0)?nrOfPages+1:nrOfPages);
 model.addAttribute('maxPages', maxPages);
 return 'members/list';
}

Given a list as a model attribute and the count of all pages(maxPages above), the list can be transformed to a simple table in a jsp, there is a nice tag library that is packaged with Spring Roo which can be used to present the pagination element in a jsp page, I have included it with the reference.

So this is the approach to pagination using JPA and Spring MVC. Spring-Data-JPA makes this even simpler, first is the repository interface to support retrieving a paginated list – in its simplest form the repository simply requires extending Spring-Data-JPA interfaces and at runtime generates the proxies which implements the real JPA calls:

import mvcsample.domain.Member;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

public interface MemberRepository extends JpaRepository<Member, Long>{
 //
}

Given this, the controller method which accesses the repository interface is also very simple:

@RequestMapping(produces='text/html')
public String list(Pageable pageable, Model model){
 Page<Member> members = this.memberRepository.findAll(pageable);
    model.addAttribute('members', members.getContent());
    float nrOfPages = members.getTotalPages();
    model.addAttribute('maxPages', nrOfPages);
 return 'members/list';
}

The controller method accepts a parameter called Pageable, this parameter is populated using a Spring MVC HandlerMethodArgumentResolver that looks for request parameters by name ‘page.page’ and ‘page.size’ and converts them into the Pageable argument. This custom HandlerMethodArgumentResolver is registered with Spring MVC this way:

<mvc:annotation-driven>
 <mvc:argument-resolvers>
  <bean class='org.springframework.data.web.PageableArgumentResolver'></bean>
 </mvc:argument-resolvers>
</mvc:annotation-driven>

the JpaRepository API takes in the pageable argument and returns a page, internally automatically populating the count of pages also which can retrieved from the Page methods. If the queries need to be explicitly specified then this can be done in a number of ways, one of which is the following:

@Query(value='select m from Member m', countQuery='select count(m) from Member m')
Page<Member> findMembers(Pageable pageable);

One catch which I could see is that that pageable’s page number is 0 indexed, whereas the one passed from the UI is 1 indexed, however the PageableArgumentResolver internally handles and converts the 1 indexed UI page parameter to the required 0 indexed value. Spring Data JPA thus makes it really simple to implement a paginated list page. I am including a sample project which ties all this together, along with the pagination tag library which makes it simple to show the paginated list.

Resources:

  • A sample projects which implements a paginated list is available here : https://github.com/bijukunjummen/spring-mvc-test-sample.git
  • Spring-Data-JPA reference: http://static.springsource.org/spring-data/data-jpa/docs/current/reference/html/

 

Reference: Spring Data JPA and pagination from our JCG partner Biju Kunjummen at the all and sundry blog.

Related Whitepaper:

Functional Programming in Java: Harnessing the Power of Java 8 Lambda Expressions

Get ready to program in a whole new way!

Functional Programming in Java will help you quickly get on top of the new, essential Java 8 language features and the functional style that will change and improve your code. This short, targeted book will help you make the paradigm shift from the old imperative way to a less error-prone, more elegant, and concise coding style that’s also a breeze to parallelize. You’ll explore the syntax and semantics of lambda expressions, method and constructor references, and functional interfaces. You’ll design and write applications better using the new standards in Java 8 and the JDK.

Get it Now!  

2 Responses to "Spring Data JPA and pagination"

  1. gdgsf says:

    codigoooo meu velho

Leave a Reply


nine − = 8



Java Code Geeks and all content copyright © 2010-2014, Exelixis Media Ltd | Terms of Use | Privacy Policy
All trademarks and registered trademarks appearing on Java Code Geeks are the property of their respective owners.
Java is a trademark or registered trademark of Oracle Corporation in the United States and other countries.
Java Code Geeks is not connected to Oracle Corporation and is not sponsored by Oracle Corporation.

Sign up for our Newsletter

20,709 insiders are already enjoying weekly updates and complimentary whitepapers! Join them now to gain exclusive access to the latest news in the Java world, as well as insights about Android, Scala, Groovy and other related technologies.

As an extra bonus, by joining you will get our brand new e-books, published by Java Code Geeks and their JCG partners for your reading pleasure! Enter your info and stay on top of things,

  • Fresh trends
  • Cases and examples
  • Research and insights
  • Two complimentary e-books