2012-06-22 8 views
5

Un mio collega ha la seguente (a quanto pare non valido) interrogazione JPQL:In JPQL JPA 2.0, quando si restituisce un oggetto NUOVO, come si può utilizzare FETCH JOINs?

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

Vale la pena notare (per più avanti in questo messaggio) che il rapporto DonationAllocation s' con un'entità Campaign è molti-a-uno, ed è contrassegnato come FetchType.LAZY. L'intento del mio collega con questa query è (tra le altre cose) garantire che a.campaign sia "gonfiato" (recuperato con fervore).

Hibernate (ovviamente solo un'implementazione JPA dei tanti), di fronte a questa query, dice:

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

Questo ha senso, come la lista di selezione contiene solo NEW DonationAllocationDTOEntity(), e la sezione 4.4.5.3 delle specifiche JPA 2.0:

L'associazione a cui fa riferimento la parte destra della clausola FETCH JOIN deve essere un'associazione o una raccolta di elementi a cui viene fatto riferimento da un'entità o incorporabile che viene restituita come risultato della query.

Così poiché non v'è alcuna "entità o incorporabile che viene restituito come risultato della query" (è un DTO costruito utilizzando l'operatore NEW), ne consegue che non è possibile un'associazione per un recupero JOIN di riferimento e quindi questa query non è valida.

In che modo, data questa limitazione, si dovrebbe costruire una query JPQL in questo caso tale che a.campaign - passata nell'espressione del costruttore - venga richiamata con entusiasmo?

risposta

2

Vorrei semplicemente selezionare l'entità e la sua associazione, e llopover i risultati per richiamare esplicitamente il costruttore DTO. Si avrebbe l'ulteriore vantaggio di controlli in fase di compilazione e il codice refactorable:

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:

Questa domanda dovrebbe anche selezionare ciò che è necessario, e evitare di selezionare l'intera entità 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') 

e si potrebbe anche aggiungere il costruttore DTO nella query se si desidera:

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

Il fatto che a.campaign sia nella clausola select dovrebbe essere sufficiente per dire a Hibernate di caricare l'entità. Almeno è così che si comporta nei miei test.

+0

Grazie. 'DonationAllocation' è attualmente un * enorme * oggetto, con troppe relazioni' EAGER'. Credo che il mio collega stia tentando di recuperare solo alcune parti di esso. Accade solo che una di quelle relazioni che * fa * in realtà voglia caricate avidamente in questa query. Con questa nuova conoscenza, cambierebbe la tua risposta? –

+0

Sì, vedere la mia risposta. Ma se caricare l'entità è un problema del genere, significa solo che le sue associazioni avide dovrebbero essere rese pigre. Preferisco rendere tutto pigro. –

Problemi correlati