Spring Данные JPA Pagination (Пейдж) с динамическими запросами

У меня есть простой запрос следующим образом: "select * from USERS". Я также использую Pageable, чтобы включить разбивку на страницы.

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

Например, если задан параметр "code", а не null, тогда запрос будет "выберите * из ПОЛЬЗОВАТЕЛЕЙ, где code =: code";

Насколько я знаю, я не могу реализовать это, используя аннотацию @Query. Я могу реализовать пользовательский репозиторий и использовать EntityManager для создания динамического запроса. Тем не менее, я не уверен, как я могу интегрировать "Пейдж" с этим, чтобы получить результаты с разбивкой по страницам.

Как я могу это достичь?

Ответ 1

Это очень легко сделать в Spring Data с использованием QueryDSL (как альтернатива критериям API). Он поддерживается из коробки с помощью следующего метода QueryDSLPredicateExecutor, где вы можете просто передать null в качестве Predicate, если не применять никаких ограничений:

Page<T> findAll(com.mysema.query.types.Predicate predicate,
                Pageable pageable)

Использование QueryDSL, возможно, не является для вас вариантом, если вы посмотрите на следующие серии руководств, вы можете получить некоторые идеи.

http://www.petrikainulainen.net/programming/spring-framework/spring-data-jpa-tutorial-part-nine-conclusions/

Сценарий, который у вас есть, фактически обсуждается автором в комментариях к части 9 его руководства.

Ответ 2

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

public class QueryDslSupport<E, Q extends EntityPathBase<E>> extends QueryDslRepositorySupport {

  public QueryDslSupport(Class<E> clazz) {
    super(clazz);
  }

  protected Page<E> readPage(JPAQuery query, Q qEntity, Pageable pageable) {
    if (pageable == null) {
      return readPage(query, qEntity, new QPageRequest(0, Integer.MAX_VALUE));
    }
    long total = query.clone(super.getEntityManager()).count(); // need to clone to have a second query, otherwise all items would be in the list
    JPQLQuery pagedQuery = getQuerydsl().applyPagination(pageable, query);
    List<E> content = total > pageable.getOffset() ? pagedQuery.list(qEntity) : Collections.<E> emptyList();
    return new PageImpl<>(content, pageable, total);
  }

}

Ответ 3

Вы должны использовать querydsl и построить свой where в зависимости от не нулевого параметра, например

BooleanBuilder where = new BooleanBuilder();
...
    if(code  != null){
        where.and(YOURENTITY.code.eq(code));
    } 

и после выполнения запроса

    JPAQuery query = new JPAQuery(entityManager).from(..)               
            .leftJoin( .. )
            ...
            .where(where)

и используйте свою собственную страницу

    MaPage<YOURENTITY> page = new MaPage<YOURENTITY>();
    page.number = pageNumber+1;

    page.content = query.offset(pageNumber*pageSize).limit(pageSize).list(...);

    page.totalResult = query.count();

Я создаю такой MyPage

public class MaPage<T> {

    public List<T> content;
    public int number;
    public Long totalResult;
    public Long totalPages;
    ...
}

он работает, но если в вашем запросе есть выборка, вы получите это предупреждение

ноябрю 21, 2014 6:48:54 org.hibernate.hql.internal.ast.QueryTranslatorImpl список
WARN: HHH000104: firstResult/maxResults, указанные с помощью коллекции; применение в памяти!

и это замедлит ваш запрос. Таким образом, решение состоит в том, чтобы получить доступ к выборке и определить @BatchSize(size=10) и использовать Hibernate.initialize(....) для извлечения данных в коллекциях и других типах объектов.

Отображать данные из связанных объектов, чтобы избежать ленивого исключения инициализации при настройке @BatchSize

Как выполнить JPAQuery с разбивкой по страницам, используя Spring Data and QueryDSL

Ответ 4

Информация здесь устарела. Попросите репозиторий выполнить QueryDslPredicateExecutor, а пейджинг - бесплатно.