我遇到问题,我在JPA中有查询。因为我有一些集合,我需要使用左连接提取或内连接提取
我的问题在于使用setFirstResult
和setMaxResult
来恢复精确数量的结果。每次我看到整个结果都会在使用maxResult后才恢复。
以前有没有办法制作maxResult?
非常感谢!
这里有更多信息:
我的问题是当我使用它时:
startIndex = 0;
maxResults = 10;
query.setFirstResult(startIndex);
query.setMaxResults(maxResults);
我在日志中看到此消息:
2011年7月7日09:52:37 org.hibernate.hql.ast.QueryTranslatorImpl list 注意:firstResult / maxResults使用collection fetch指定; 在记忆中应用!
我看到200个结果回来了(在日志中),然后在HashSet中我终于得到了10个结果。
它在内存中似乎带回了200结果,并且在内存中应用了maxResults。
我正在搜索是否有任何方法可以获取并限制结果数量。
我使用了一种解决方法,我首先询问我的订单的ID,没有任何提取,使用了maxResult。 一切都很完美,它使用了极限指令。 在我使用带有fetch的“大”查询后,将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
答案 0 :(得分:29)
TL; DR Hibernate不知道获取指定数量的Order对象所需的拼合,连接查询的行数,因此必须将整个查询加载到内存中。请参阅下面的解释。
要理解为什么Hibernate会这样做,你需要了解Hibernate如何处理JPA实体所涉及的ORM(对象关系映射)。
为您的订单考虑一组简化的实体。类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将此查询作为'flattened'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个货件对象,每个对象都引用同一个Order对象,该对象本身将包含4行。
不,再错了!实际上,您将获得2个Shipment对象,每个对象引用相同的Order对象,其中包含 8 行!是的,行在订单中重复!是的,即使您指定了DISTINCT子句,也就是这样。如果您在SO或其他地方(最值得注意的是Hibernate论坛)上研究此问题,您会发现这实际上是一个功能而不是一个bug,根据Hibernate的功能。有些人实际上 想要 这种行为!
去图。
答案 1 :(得分:2)
这取决于所使用的数据库引擎......
我遇到与Derby DB相同的问题setFirstResult
/ setMaxResults
未被使用。
ROW_NUMBER() OVER()...
似乎是解决方案。
不幸的是,我没有发现如何使用JPA生成此类请求而不会在SQL中使用整个请求。
答案 2 :(得分:0)
setMaxResult(10)用另一个限制结果包装查询,例如对于oracle:
选择* from(“你的查询在这里”),其中rownum&lt; 10
今天我在调查查询时发现这个问题需要64秒,现在需要1秒钟。
...对于criteria.setProjection(Projections.rowCount())也一样.setMaxResults(10)