Ответ 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. Некоторые люди действительно хотят это поведение!
Показать рисунок.