在JPA 2.0 JPQL中,当一个返回一个NEW对象时,如何使用FETCH JOIN?

时间:2012-06-22 21:39:53

标签: jpa jpql

我的一位同事有以下(显然无效)JPQL查询:

SELECT NEW com.foobar.jpa.DonationAllocationDTOEntity(a.id, a.campaign, a.campAppeal, a.campDivision, a.divisionFund)
FROM DonationAllocation a JOIN a.donation d JOIN a.allocationType t
JOIN FETCH a.campaign
WHERE d.id = :donationId
AND (t.code = 'Pledge' OR t.code = 'MatchingPledge')

值得注意的是(此消息的后面部分)DonationAllocationCampaign实体的关系是多对一的,并标记为FetchType.LAZY。我的同事对此查询的意图是(除其他事项外)确保a.campaign“膨胀”(急切地获取)。

Hibernate(显然只是一个JPA实现的几个),当遇到这个查询时,说:

  

query specified join fetching, but the owner of the fetched association was not present in the select list

这是有道理的,因为选择列表仅包含NEW DonationAllocationDTOEntity(),JPA 2.0规范的第4.4.5.3节说:

  

FETCH JOIN子句右侧引用的关联必须是从实体引用的关联或元素集合,或者作为查询结果返回的可嵌入对象。

因为没有“作为查询结果返回的实体或嵌入式”(它是使用NEW运算符构造的DTO),因此FETCH JOIN与之无关联。引用,因此该查询无效。

在给定此限制的情况下,如果在这种情况下构造一个JPQL查询,以便传递给构造函数表达式的a.campaign - 是如何急切地获取的?

1 个答案:

答案 0 :(得分:2)

我只需选择实体及其关联,然后重复结果以明确地调用DTO构造函数。您将拥有编译时检查和可重构代码的额外优势:

select a from DonationAllocation a 
JOIN a.donation d 
JOIN a.allocationType t
JOIN FETCH a.campaign
WHERE d.id = :donationId
AND (t.code = 'Pledge' OR t.code = 'MatchingPledge')

...

for (DonationAllocation a : list) {
    result.add(new DonationAllocationDTOEntity(a.id, 
                                               a.campaign,
                                               a.campAppeal, 
                                               a.campDivision, 
                                               a.divisionFund));
}

编辑:

此查询还应选择所需内容,并避免选择整个DonationAllocation实体:

select a.id, a.campaign, a.campAppeal, a.campDivision, a.divisionFund
from DonationAllocation a 
JOIN a.donation d 
JOIN a.allocationType t
WHERE d.id = :donationId
AND (t.code = 'Pledge' OR t.code = 'MatchingPledge')

如果需要,您可以在查询中添加DTO构造函数:

select new com.foobar.jpa.DonationAllocationDTOEntity(a.id, a.campaign, a.campAppeal, a.campDivision, a.divisionFund)
from DonationAllocation a 
JOIN a.donation d 
JOIN a.allocationType t
WHERE d.id = :donationId
AND (t.code = 'Pledge' OR t.code = 'MatchingPledge')

a.campaign在select子句中的事实应足以告诉Hibernate加载实体。至少这是我在测试中的表现。