Подтвердить что ты не робот

Hibernate - разбиение на страницы HQL

Это проблема, похожая на: HQL - идентификатор строки для разбивки на страницы

Я пытаюсь реализовать разбиение на страницы с помощью HQL. У меня есть база данных PostgreSQL.

int elementsPerBlock = 10;
int page = 2; //offset = 2*10

String sqlQuery = "FROM Messages AS msg " +
                  " LEFT JOIN FETCH msg.commands AS cmd " +   
                  "ORDER BY msg.identifier ASC" ;

Query query = session.createQuery( sqlQuery )
                     .setFirstResult( elementsPerBlock * ( (page-1) +1 ) )
                     .setMaxResults( elementsPerBlock );

Что происходит, так это то, что Hibernate извлекает ВСЕ сообщения и возвращает нужные после того, как они были загружены.

Таким образом, Hibernate извлекает 210000 объектов вместо 30, которые возвращаются (каждое сообщение имеет ровно 2 команды).

Есть ли способ уменьшить накладные расходы в 7000 раз?

edit: Я пытаюсь добавить .setFetchSize( elementsPerBlock ). Это не помогло.

edit 2: созданный SQL-запрос:

select ... 
from schemaName.messages messages0_ 
left outer join schemaName.send_commands commands1_ 
on messages0_.unique_key=commands1_.message_key 
order by messages0_.unique_identifier ASC

Абсолютно нет LIMIT или OFFSET

4b9b3361

Ответ 1

В спецификации JPA 2.0, раздел 3.8.6 Выполнение запросов,

Эффект применения setMaxResults или setFirstResult к запросу, включающему получать соединения над коллекциями undefined.

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

То, что я обычно делал, используется отдельным запросом, чтобы получить идентификаторы желаемых объектов и передать это в запрос с помощью соединения fetch.

Ответ 2

Я использую это решение:

/**
 * @param limitPerPage
 * @param page
 * @return
 */
public List<T> searchByPage(int limitPerPage, int page, String entity) {
    String sql = "SELECT t FROM " + entity + " t";
    Query query = em.createQuery(sql)
            .setFirstResult(calculateOffset(page, limitPerPage))
            .setMaxResults(limitPerPage);
    return query.getResultList();
}

/**
 * @param page
 * @return
 */
private int calculateOffset(int page, int limit) {
    return ((limit * page) - limit);
}

Добро пожаловать.

Ответ 3

Скорее всего, если вы создадите свой собственный запрос с помощью HQL, методы построения запросов не смогут разобрать пользовательский запрос hql и изменить его. Поэтому вы должны поместить свой оператор LIMIT ?, ? в конце вашего запроса HQL и привязать параметры смещения.

Ответ 4

Поскольку вы не фильтруете результирующий набор по некоторым атрибутам командного объекта, вы также можете избежать соединения SQL и настроить ленивую выборку для команд сообщений. Без объединения Hibernate будет использовать возможности поискового вызова базы данных.

Однако вы должны заботиться о проблеме N + 1 seletcs, т.е. избегать одиночного выбора для каждого атрибута lazily fetched. Это можно избежать, установив свойство пакетного размера в вашем сопоставлении спящего режима или глобально в hibernate.default_batch_fetch_size в настройках спящего режима.

Например: если вы выбрали 100 объектов сообщений в сеансе Hibernate и задали размер пакета 10, Hibernate будет получать 10 ассоциаций команд из 10 различных объектов сообщений при первом вызове getCommands() объекта сообщения. Количество запросов уменьшается до 10 плюс исходное сообщение.

Посмотрите здесь: http://java.dzone.com/articles/hibernate-tuning-queries-using?page=0,1 Автор сравнивает различные стратегии выборки для простого примера

Ответ 5

Мы можем достичь разбивки на страницы, используя интерфейс Запрос и критерии:

Разбиение страницы с использованием интерфейса запроса:

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

1. Запрос setFirstResult (int startPosition): Этот метод принимает целое число, которое представляет первую строку в вашем результирующем наборе, начиная с строки 0.

2. Запрос setMaxResults (int maxResult): Этот метод сообщает Hibernate получить фиксированное число maxResults объектов. Используя вышеописанные два метода, мы можем создать пейджинговый компонент в нашем веб-приложении или приложении Swing.

Пример:

Query query = session.createQuery("FROM Employee");
query.setFirstResult(5);
query.setMaxResults(10);
List<Employee> list = query.list();
for(Employee emp: list) {            
   System.out.println(emp);
}

Разбиение страницы с использованием интерфейса критериев:

Существует два метода интерфейса Criteria для разбивки на страницы.

1. Критерии setFirstResult (int firstResult):

Установите первый результат, который будет извлечен.

2. Элемент списка Criteria setMaxResults (int maxResults):

Установите ограничение на количество возвращаемых объектов.

Пример:

Criteria criteria = session.createCriteria(Employee.class);
criteria.setFirstResult(5);
criteria.setMaxResults(10);            
List<Employee> list = criteria.list();
for(Employee emp: list) {            
    System.out.println(emp);
}

Ответ 6

Я думаю, что ваше первоначальное исключение не правильно.

В результате Hibernate извлекает ВСЕ сообщения и> возвращает необходимые после их загрузки.

Что происходит во время обработки запроса, так это то, что setFirstResult (calculateOffset (page, limitPerPage)) переводится в OFFSET, а setMaxResults (limitPerPage) переводится в LIMIT