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:

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:

public   interface  PersonService   {

      /**
     * 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   org.springframework.data.jpa.domain.Specification ;

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.Logger ;
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.Before ;
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.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值