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