org.hibernate.hql.ast.QueryTranslatorImpl list ВНИМАНИЕ: firstResult / maxResults, указанных в выборке коллекции; применяя в памяти - PullRequest
13 голосов
/ 07 июня 2011

Я столкнулся с проблемой, у меня есть запрос в 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 list ВНИМАНИЕ: firstResult / maxResults указывается в выборке коллекции;применение в памяти!

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

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

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

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

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

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

Ответы [ 3 ]

29 голосов
/ 24 мая 2012

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

Чтобы понять, почему Hibernate делает это, вам необходимо понять, как Hibernate использует ORM (объектно-реляционное отображение) для объектов 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 подобъектов.

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

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

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

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


ОБНОВЛЕНИЕ: Недавно я обнаружил небольшую «ошибку» с вышеприведенным.

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

Сущность Shipment будет иметь @ManyToOne связь с сущностью Order.

Допустим, у вас есть 2 Отправления для одного и того же Заказа с 4 строками.

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

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

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

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

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

Иди на цифру.

2 голосов
/ 22 июля 2011

Это зависит от используемого движка БД ...

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

ROW_NUMBER() OVER()... похоже, это решение.

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

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

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

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

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

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