我遇到了一个问题,我在JPA中有一个查询。因为我有一些集合,所以我需要使用左连接获取或内部连接获取
我的问题是使用setFirstResult和setMaxResult,以便带回准确的结果数。每次我看到整个结果都带回来,并且只有在使用maxResult之后。
setFirstResult
setMaxResult
有什么办法可以使maxResult之前吗?
非常感谢 !
这是更多信息:
我的问题是当我使用它时:
startIndex = 0; maxResults = 10; query.setFirstResult(startIndex); query.setMaxResults(maxResults);
我在日志中看到此消息:
2011年6月7日09:52:37 org.hibernate.hql.ast.QueryTranslatorImpl列表注意:用集合访存指定的firstResult / maxResults;申请内存!
我看到200结果返回(在日志中),在HashSet中之后,我终于得到了10个结果。
在内存中似乎恢复了200结果,并在内存中应用了maxResults之后。
我正在搜索是否有任何方法可以获取和限制结果数。
我使用了一种解决方法,我使用maxResult进行了第一个查询,以询问订单ID,但未进行任何提取。一切正常,它使用了极限指令。在对提取使用“大”查询并将结果限制在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
TL; DR Hibernate不知道它需要获取指定数量的Order对象的扁平化联合查询的多少行,因此它必须将整个查询加载到内存中。请参阅下面的说明。
要了解为什么Hibernate这样做,您需要了解Hibernate如何处理JPA实体所涉及的ORM(对象关系映射)。
为您的订单考虑一套简化的实体。类Order包含2个字段:number和customerId和订单行列表。类OrderLine包含productCode和quantity字段,以及uid对父Order 的键和引用。
Order
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
该Shipment实体将@ManyToOne与该实体有关联Order。
@ManyToOne
假设您有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的强大功能,您会发现它实际上是一个 功能, 而不是错误。有些人实际上 想要 这种行为!
去搞清楚。