Hibernate - HQL нумерация страниц - PullRequest
19 голосов
/ 14 июня 2011

Эта проблема похожа на: 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

Абсолютно без предела или смещения

Ответы [ 5 ]

15 голосов
/ 14 июня 2011

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

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

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

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

7 голосов
/ 13 мая 2016

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

/**
 * @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);
}

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

1 голос
/ 16 мая 2014

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

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

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

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:

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

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

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

2.Элемент списка Критерии 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);
}
1 голос
/ 07 ноября 2011

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

Однако вам нужно позаботиться о проблеме N + 1 селектов , т. Е. Обойтись без единого выбора для каждого атрибута команды , который выбирается лениво. Этого можно избежать, установив свойство batch-size в своем отображении спящего режима или глобально свойство hibernate.default_batch_fetch_size в настройках спящего режима.

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

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

1 голос
/ 14 июня 2011

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...