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的强大功能,您会发现它实际上是一个 功能,
而不是错误。有些人实际上 想要 这种行为!
去搞清楚。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)