Давайте начнем с классического способа 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;@Repositorypublic 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.
