【问题标题】:In JPA 2.0 JPQL, when one returns a NEW object, how may one make use of FETCH JOINs?在 JPA 2.0 JPQL 中,当返回一个 NEW 对象时,如何使用 FETCH JOIN?
【发布时间】:2012-06-25 05:48:57
【问题描述】:

我的一位同事有以下(显然无效)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——传递到构造函数表达式中——被急切地获取?

【问题讨论】:

    标签: jpa jpql


    【解决方案1】:

    我将简单地选择实体及其关联,然后翻转结果以显式调用 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 加载实体。至少在我的测试中它是这样的。

    【讨论】:

    • 谢谢。 DonationAllocation 当前是一个 巨大的 对象,有太多的 EAGER 关系。我相信我的同事正试图只撤回其中的某些部分。碰巧的是,他确实的这些关系之一实际上希望在此查询中急切地加载。有了这些新知识,这会改变你的答案吗?
    • 是的,看我的回答。但是,如果加载实体是一个问题,那只是意味着它的急切关联应该是惰性的。我更喜欢让一切都变得懒惰。
    猜你喜欢
    • 2015-07-17
    • 1970-01-01
    • 2022-11-12
    • 1970-01-01
    • 1970-01-01
    • 2013-01-11
    • 1970-01-01
    • 2020-09-17
    • 2014-05-10
    相关资源
    最近更新 更多