Рубрики
Без рубрики

Разбиение на страницы JPA

Разбиение на страницы в JPA – как правильно использовать JQL и API критериев для разбиения на страницы.

Автор оригинала: Eugen Paraschiv.

1. Обзор

В этой статье показано, как реализовать разбиение на страницы в Java Persistence API .

В нем объясняется, как выполнять подкачку с помощью базового JQL и более типобезопасных API на основе критериев, обсуждая преимущества и известные проблемы каждой реализации.

Дальнейшее чтение:

Разбиение на страницы с весенним отдыхом и таблицей AngularJS

Spring JPA – Несколько баз данных

Spring Data JPA @Query

2. Разбиение на страницы С помощью JQL и API setFirstResult(), setMaxResults()

Самый простой способ реализовать разбиение на страницы – использовать язык запросов Java – создать запрос и настроить его с помощью setMaxResults | и s e tFirstResult :

Query query = entityManager.createQuery("From Foo");
int pageNumber = 1;
int pageSize = 10;
query.setFirstResult((pageNumber-1) * pageSize); 
query.setMaxResults(pageSize);
List  fooList = query.getResultList();

API прост:

  • setFirstResult(int ) : Устанавливает положение смещения в результирующем наборе для начала разбиения на страницы
  • setMaxResults(int) : Задает максимальное количество объектов, которые должны быть включены на страницу

2.1. Общее количество и Последняя страница

Для более полного решения по разбиению на страницы нам также нужно будет получить общее количество результатов :

Query queryTotal = entityManager.createQuery
    ("Select count(f.id) from Foo f");
long countResult = (long)queryTotal.getSingleResult();

Вычисление последней страницы также очень полезно:

int pageSize = 10;
int pageNumber = (int) ((countResult / pageSize) + 1);

Обратите внимание, что этот подход к получению общего количества результирующего набора требует дополнительного запроса (для количества).

3. Разбиение на страницы С помощью JQL С использованием идентификаторов сущностей

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

Query queryForIds = entityManager.createQuery(
  "Select f.id from Foo f order by f.lastName");
List fooIds = queryForIds.getResultList();
Query query = entityManager.createQuery(
  "Select f from Foo e where f.id in :ids");
query.setParameter("ids", fooIds.subList(0,10));
List fooList = query.getResultList();

Наконец, также обратите внимание, что для получения полных результатов требуется 2 отдельных запроса.

4. Разбиение На Страницы С Помощью JPA С Использованием API Критериев

Далее давайте рассмотрим, как мы можем использовать API критериев JPA для реализации разбиения на страницы:

int pageSize = 10;
CriteriaBuilder criteriaBuilder = entityManager
  .getCriteriaBuilder();
CriteriaQuery criteriaQuery = criteriaBuilder
  .createQuery(Foo.class);
Root from = criteriaQuery.from(Foo.class);
CriteriaQuery select = criteriaQuery.select(from);
TypedQuery typedQuery = entityManager.createQuery(select);
typedQuery.setFirstResult(0);
typedQuery.setMaxResults(pageSize);
List fooList = typedQuery.getResultList();

Это полезно, когда цель состоит в создании динамических, отказоустойчивых запросов. В отличие от “жестко закодированных”, “строковых” запросов JQL или HQL, критерии JPA уменьшают сбои во время выполнения, поскольку компилятор динамически проверяет наличие ошибок запроса.

С критериями JPA получение общего количества сущностей достаточно просто:

CriteriaQuery countQuery = criteriaBuilder
  .createQuery(Long.class);
countQuery.select(criteriaBuilder.count(
  countQuery.from(Foo.class)));
Long count = entityManager.createQuery(countQuery)
  .getSingleResult();

Конечным результатом является полное решение для разбиения на страницы , использующее API критериев JPA:

int pageNumber = 1;
int pageSize = 10;
CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();

CriteriaQuery countQuery = criteriaBuilder
  .createQuery(Long.class);
countQuery.select(criteriaBuilder
  .count(countQuery.from(Foo.class)));
Long count = entityManager.createQuery(countQuery)
  .getSingleResult();

CriteriaQuery criteriaQuery = criteriaBuilder
  .createQuery(Foo.class);
Root from = criteriaQuery.from(Foo.class);
CriteriaQuery select = criteriaQuery.select(from);

TypedQuery typedQuery = entityManager.createQuery(select);
while (pageNumber < count.intValue()) {
    typedQuery.setFirstResult(pageNumber - 1);
    typedQuery.setMaxResults(pageSize);
    System.out.println("Current page: " + typedQuery.getResultList());
    pageNumber += pageSize;
}

5. Заключение

В этой статье были рассмотрены основные параметры разбиения на страницы, доступные в JPA.

Некоторые из них имеют недостатки, в основном связанные с производительностью запросов, но они обычно компенсируются улучшенным контролем и общей гибкостью.

Реализацию этого весеннего руководства по JPA можно найти в проекте GitHub – это проект на основе Maven, поэтому его должно быть легко импортировать и запускать как есть.