Список org.hibernate.hql.ast.QueryTranslatorImpl ВНИМАНИЕ: firstResult/maxResults, указанные с помощью коллекции; применение в памяти

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

Моя проблема заключается в использовании setFirstResult и setMaxResult, чтобы вернуть точное количество результатов. каждый раз, когда я вижу, что весь результат возвращается, И только ПОСЛЕ использования maxResult.

Есть ли способ сделать maxResult раньше?

Спасибо большое!

здесь больше информации:

моя проблема в том, когда я использую это:

startIndex = 0;
maxResults = 10;
query.setFirstResult(startIndex);
query.setMaxResults(maxResults);

Я вижу это сообщение в своем журнале:

7 июня 2011 г. 09:52:37 org.hibernate.hql.ast.QueryTranslatorImpl список ВНИМАНИЕ: firstResult/maxResults, указанные с помощью коллекции; применение в памяти!

Я вижу, что результат 200 возвращается (в журнале), а затем в HashSet у меня есть, наконец, 10 результат, который я задаю.

его, кажется, в памяти возвращает результат 200 и после применения maxResults в памяти.

Я ищу, есть ли способ получить и ограничить количество результатов.

Я использовал обходной путь, я делаю первый запрос, чтобы спросить идентификатор моего заказа, без какой-либо выборки, использовал maxResult. все работает отлично, он использовал инструкцию ограничения. После того, как я использую свой "большой" запрос с помощью fetch и ограничиваю результат внутри списка id, возвращаем его в первый.

вот он мой полный запрос без моего обходного пути (обратите внимание, что нет ограничений, генерируемых как разговор от @Bozho):

select o from Order  o
   left join fetch o.notes note
   left join fetch o.orderedBy orderedBy
   left join fetch orderedBy.address addressOrdered 
   left join fetch orderedBy.language orderedByLg 
   left join fetch orderedByLg.translations orderedByLgTtrad
   left join fetch o.deliveredTo deliveredTo 
   left join fetch deliveredTo.address addressDelivered 
   left join fetch deliveredTo.language deliveredToLg
   left join fetch deliveredToLg.translations 
   left join fetch o.finalReceiptPlace finalReceiptPlace
   left join fetch finalReceiptPlace.address addressFinalReceiptPlace 
   left join fetch finalReceiptPlace.language finalReceiptPlaceLg 
   left join fetch finalReceiptPlaceLg.translations
   inner join fetch o.deliveryRoute delivery
   left join fetch delivery.translations
   inner join fetch o.type orderType
   left join fetch orderType.translations 
   inner join fetch o.currency currency
   left join fetch currency.translations
   left join fetch o.attachments 
   left join fetch note.origin orig
   left join fetch orig.translations
   left join fetch o.supplier sup  
   left join fetch sup.department dep 
   left join fetch o.stateDetail stateD
   inner join fetch stateD.state stat  
where 1=1 and o.entryDate >= :startDat

Ответ 1

TL; DR Hibernate не знает, сколько строк сплющенного, объединенного запроса нужно получить указанное количество объектов Order, поэтому он должен загрузить весь запрос в память. Ниже приводится пояснение.

Чтобы понять, почему Hibernate делает это, вам нужно понять, как Hibernate выполняет ORM (Object- Реляционное сопоставление) для объектов JPA.

Рассмотрим упрощенный набор объектов для вашего заказа. Класс Order содержит 2 поля: number и customerId и список строк порядка. Класс OrderLine содержит поля productCode и quantity, а также ключ uid и ссылку на родительский заказ.

Эти классы могут быть определены следующим образом:

@Entity
@Table(name = "ORDER")
public class Order {
    @ID
    @Column(name = "NUMBER")
    private Integer number;
    @Column(name = "CUSTOMER_ID")
    private Integer customerId;
    @OneToMany(mappedBy = "order", fetch = FetchType.LAZY)
    @OrderBy
    private List<OrderLine> orderLineList;

    .... // Rest of the class
}

@Entity
@Table(name = "ORDER_LINE")
public class OrderLine
{
    @ID
    @Column(name = "UID")
    private Integer uid;
    @Column(name = "PRODUCT_CODE")
    private Integer productCode;
    @Column(name = "QUANTITY")
    private Integer quantity;
    @Column(name = "ORDER_NUMBER")
    private Integer orderNumber;
    @ManyToOne(fetch = FetchType.LAZY, optional = false)
    @JoinColumn(name = "ORDER_NUMBER", referencedColumnName = "NUMBER", insertable = false, updatable = false)
    private Order order;

    .... // Rest of the class
}

Теперь, если вы выполнили следующий запрос JPQL для этих объектов:

SELECT o FROM Order o LEFT JOIN FETCH o.orderLineList

то Hibernate выполняет этот запрос как "сплющенный" SQL-запрос, похожий на следующий:

SELECT o.number, o.customer_id, ol.uid, ol.product_code, ol.quantity, ol.order_number
FROM order o LEFT JOIN order_line ol ON order_line.order_number = order.number

который даст такой результат:

| o.number | o.customer_id | ol.uid | ol.product_code | ol.quantity |
|==========|===============|========|=================|=============|
| 1        | 123           | 1      | 1111            | 5           |
| 1        | 123           | 2      | 1112            | 6           |
| 1        | 123           | 3      | 1113            | 1           |
| 2        | 123           | 4      | 1111            | 2           |
| 2        | 123           | 5      | 1112            | 7           |
| 3        | 123           | 6      | 1111            | 6           |
| 3        | 123           | 7      | 1112            | 5           |
| 3        | 123           | 8      | 1113            | 3           |
| 3        | 123           | 9      | 1114            | 2           |
| 3        | 123           | 10     | 1115            | 9           |
...etc

который Hibernate будет использовать для "восстановления" Order объектов с прикрепленными списками объектов OrderLine sub-.

Однако, поскольку количество строк порядка на порядок является случайным, Hibernate не может узнать, сколько строк этого запроса требуется для получения указанного максимального количества объектов Order. Поэтому он должен взять весь запрос и создать объекты в памяти до тех пор, пока он не будет иметь правильное количество, прежде чем отбрасывать остальную часть набора результатов. Предупреждение о регистрации в журнале указывает на это:

ATTENTION: firstResult/maxResults specified with collection fetch; applying in memory!

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

Кстати, я скажу теперь, что это в основном просто теория с моей стороны, и я не знаю, как работает настоящий Hibernate-код. Большинство из них можно почерпнуть из журналов, когда у вас есть Hibernate, регистрирующий SQL-запросы, которые он генерирует.


UPDATE: Недавно я обнаружил немного "gotcha" с приведенным выше.

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

Объект Shipment будет иметь ассоциацию @ManyToOne с объектом Order.

Скажем, у вас есть 2 отправления для того же ордера, который имеет 4 строки.

Если вы выполняете запрос JPQL для следующего:

SELECT s FROM Shipment s LEFT JOIN s.order o LEFT JOIN FETCH o.orderLineList

Вы ожидали бы (или, по крайней мере, я), чтобы вернуть два объекта доставки, каждый со ссылкой на тот же объект Order, который сам будет содержать 4 строки.

Нет, снова не так! Фактически вы получаете 2 объекта "Отгрузка", каждый из которых ссылается на тот же объект "Заказ", который содержит строки 8! Да, строки дублируются в Ордене! И да, это даже если вы укажете предложение DISTINCT.

Если вы исследуете эту проблему здесь, в SO или где-либо еще (особенно в форумах Hibernate), вы обнаружите, что на самом деле это функция, а не ошибка в соответствии с полномочиями Hibernate. Некоторые люди действительно хотят это поведение!

Показать рисунок.

Ответ 2

Это зависит от используемого механизма БД...

У меня та же проблема с Derby DB setFirstResult/setMaxResults не используются.

ROW_NUMBER() OVER()... представляется решением.

К сожалению, я не могу найти, как я могу генерировать такие запросы с помощью JPA, не выполняя весь запрос в SQL.

Ответ 3

setMaxResult (10) завершает запрос другим ограничивающим результатом, например, для oracle:
выберите * from ( "ваш запрос здесь" ), где rownum < 10

Я узнал об этом сегодня во время исследования запроса, которому потребовалось 64 секунды, теперь требуется 1 секунда.

... то же самое для criteria.setProjection(Projections.rowCount()). setMaxResults (10)