In JPA 2.0 JPQL, when one returns a NEW object, how may one make use of FETCH JOINs?

别等时光非礼了梦想. 提交于 2019-12-23 09:59:49

问题


A colleague of mine has the following (apparently invalid) JPQL query:

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')

It is worth noting (for later in this message) that DonationAllocation's relationship with a Campaign entity is many-to-one, and is marked as FetchType.LAZY. My colleague's intent with this query is to (among other things) ensure that a.campaign is "inflated" (eagerly fetched).

Hibernate (obviously just one JPA implementation of several), when faced with this query, says:

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

This makes sense, as the select list contains only NEW DonationAllocationDTOEntity(), and section 4.4.5.3 of the JPA 2.0 specification says:

The association referenced by the right side of the FETCH JOIN clause must be an association or element collection that is referenced from an entity or embeddable that is returned as a result of the query.

So since there is no "entity or embeddable that is returned as a result of the query" (it's a DTO constructed using the NEW operator), it follows that there is no possible association for a FETCH JOIN to reference, and hence this query is invalid.

How, given this limitation, should one construct a JPQL query in this case such that a.campaign--passed into the constructor expression--is fetched eagerly?


回答1:


I would simply select the entity and its association, and llopover the results to invoke the DTO constructor explicitely. You would have the additional advantage of compile-time checks and refactorable code:

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));
}

EDIT:

This query should also select what's needed, and avoid selecting the whole DonationAllocation entity:

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')

and you might just add the DTO constructor in the query if you want:

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')

The fact the a.campaign is in the select clause should be sufficient to tell Hibernate to load the entity. At least that's how it behaves in my tests.



来源:https://stackoverflow.com/questions/11164496/in-jpa-2-0-jpql-when-one-returns-a-new-object-how-may-one-make-use-of-fetch-jo

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!