Статьи

Spring Data JPA и нумерация страниц

Давайте начнем с классического способа JPA для поддержки нумерации страниц. Рассмотрим простой класс домена – «Член» с атрибутами имя, фамилия. Для поддержки разбиения на страницы в списке членов способ JPA состоит в том, чтобы поддерживать метод поиска, который принимает смещение первого результата (firstResult) и размер результата (maxResults) для извлечения, таким образом:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
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();
 }
}

Дополнительный API, который возвращает количество записей, необходим для определения количества страниц в списке сущностей, как показано выше. Учитывая этот API, из пользовательского интерфейса обычно требуются два параметра:

  • текущая страница отображается (скажем, «page.page»)
  • размер списка на странице (скажем, «page.size»)

Контроллер будет отвечать за преобразование этих входов в тот, который требуется JPA – firstResult и maxResults таким образом:

01
02
03
04
05
06
07
08
09
10
@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';
}

Учитывая список в качестве атрибута модели и количество всех страниц (maxPages выше), список может быть преобразован в простую таблицу в jsp, есть хорошая библиотека тегов, которая поставляется вместе с Spring Roo, которая может использоваться для представления элемент пагинации на странице JSP, я включил его со ссылкой.

Так что это подход к нумерации страниц с использованием JPA и Spring MVC. Spring-Data-JPA делает это еще проще , во-первых, интерфейс репозитория для поддержки извлечения разбитого на страницы списка – в простейшем виде хранилище просто требует расширения интерфейсов Spring-Data-JPA и во время выполнения генерирует прокси, которые реализуют реальные вызовы JPA:

1
2
3
4
5
6
7
8
import mvcsample.domain.Member;
 
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
 
public interface MemberRepository extends JpaRepository<Member, Long>{
 //
}

Учитывая это, метод контроллера, который обращается к интерфейсу хранилища, также очень прост:

1
2
3
4
5
6
7
8
@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';
}

Метод контроллера принимает параметр с именем Pageable, этот параметр заполняется с помощью Spring MVC HandlerMethodArgumentResolver, который ищет параметры запроса по именам «page.page» и «page.size» и преобразует их в аргумент Pageable. Этот пользовательский HandlerMethodArgumentResolver зарегистрирован в Spring MVC следующим образом:

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

API-интерфейс JpaRepository принимает аргумент pageable и возвращает страницу, автоматически заполняя количество страниц, которое также можно получить из методов Page. Если запросы должны быть явно указаны, то это можно сделать несколькими способами, одним из которых является следующее:

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

Один улов, который я мог видеть, состоит в том, что номер страницы этой табличной страницы индексируется 0, тогда как номер, передаваемый из пользовательского интерфейса, индексируется 1, однако PageableArgumentResolver внутренне обрабатывает и преобразует 1-индексированный параметр страницы пользовательского интерфейса в требуемое 0 индексированное значение. Таким образом, Spring Data JPA упрощает реализацию страницы списка с разбивкой по страницам. Я включаю пример проекта, который связывает все это вместе, а также библиотеку тегов пагинации, которая упрощает показ разбитого на страницы списка.

Ресурсы:

  • Примеры проектов, которые реализуют нумерованный список, доступны здесь : https://github.com/bijukunjummen/spring-mvc-test-sample.git.
  • Ссылка на Spring-Data-JPA : http://static.springsource.org/spring-data/data-jpa/docs/current/reference/html/

Ссылка: Spring Data JPA и нумерация страниц от нашего партнера JCG Биджу Кунджуммен в блоге all and sundry.