org.hibernate.hql.ast.QueryTranslatorImpl列表注意:用集合访存指定的firstResult maxResults;申请内存

org.hibernate.hql.ast.QueryTranslatorImpl列表注意:用集合访存指定的firstResult maxResults;申请内存,第1张

org.hibernate.hql.ast.QueryTranslatorImpl列表注意:用集合访存指定的firstResult /maxResults;申请内存

TL; DR
Hibernate不知道它需要获取指定数量的Order对象的扁平化联合查询的多少行,因此它必须将整个查询加载到内存中。请参阅下面的说明。

要了解为什么Hibernate这样做,您需要了解Hibernate如何处理JPA实体所涉及的ORM(对象关系映射)。

为您的订单考虑一套简化的实体。类

Order
包含2个字段:
number
customerId
和订单行列表。类
OrderLine
包含
productCode
quantity
字段,以及
uid
对父Order
的键和引用。

这些类可以这样定义:

@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_pre, ol.quantity, ol.order_numberFROM order o LEFT JOIN order_line ol ON order_line.order_number = order.number

这将产生如下结果:

| o.number | o.customer_id | ol.uid | ol.product_pre | 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个装运对象,每个对象都引用同一个Order对象,该对象本身将包含4行。

不,又错了!实际上,您将获得2个Shipment对象,每个对象都引用相同的Order对象,其中包含 8
行!是的,订单中的行重复!是的,即使您指定了DISTINCT子句也是如此。

如果您在SO或其他地方(最著名的是Hibernate论坛)研究此问题,那么根据Hibernate的强大功能,您会发现它实际上是一个 功能,
而不是错误。有些人实际上 想要 这种行为!

去搞清楚。



欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/5027737.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-11-15
下一篇 2022-11-15

发表评论

登录后才能评论

评论列表(0条)

保存