2012-06-22 15 views
5

Un colega mío tiene la siguiente (al parecer no válido) JPQL consulta:En JPA 2.0 JPQL, cuando uno devuelve un objeto NUEVO, ¿cómo se puede usar 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 señalar (para más adelante en este mensaje) que la relación DonationAllocation 's con una entidad Campaign es muchos-a-uno, y está marcado como FetchType.LAZY. La intención de mi colega con esta consulta es (entre otras cosas) asegurarse de que a.campaign esté "inflado" (buscado con impaciencia).

Hibernate (obviamente sólo una aplicación APP de varias), cuando se enfrentan a esta consulta, dice:

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

Esto tiene sentido, ya que la lista de selección contiene sólo NEW DonationAllocationDTOEntity(), y la sección 4.4.5.3 de la especificación JPA 2.0 dice:

La asociación a la que se hace referencia en el lado derecho de la cláusula FETCH JOIN debe ser una asociación o colección de elementos a la que se hace referencia desde una entidad o incrustable que se devuelve como resultado de la consulta.

lo tanto, ya no hay una "entidad o integrable que se devuelve como resultado de la consulta" (es un DTO construida usando el operador NEW), se deduce que no existe una asociación posible que un FETCH se unen para hacer referencia a y, por lo tanto, esta consulta no es válida.

¿Cómo, dada esta limitación, debería uno construir una consulta JPQL en este caso de manera que a.campaign --pasado en la expresión del constructor - se busque con entusiasmo?

Respuesta

2

Simplemente seleccionaría la entidad y su asociación, y replantearía los resultados para invocar el constructor de DTO explícitamente. Usted tendría la ventaja adicional de comprobaciones en tiempo de compilación y el código 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:

Esta consulta también debe seleccionar lo que se necesita, y evitar la selección de toda la entidad 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') 

y puede agregar el constructor DTO en la consulta si lo desea:

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

El hecho de que a.campaign esté en la cláusula de selección debería ser suficiente para decirle a Hibernate que cargue la entidad. Al menos así es como se comporta en mis pruebas.

+0

Gracias. 'DonationAllocation' es actualmente un objeto * huge *, con demasiadas relaciones' EAGER'. Creo que mi colega está intentando retirar solo ciertas partes del mismo. Da la casualidad de que una de esas relaciones que * él * realmente desea cargar con entusiasmo en esta consulta. Con este nuevo conocimiento, ¿eso cambiaría tu respuesta? –

+0

Sí, ver mi respuesta. Pero si cargar la entidad es un problema así, simplemente significa que sus asociaciones ansiosas deberían ser flojas. Prefiero hacer que todo sea flojo. –

Cuestiones relacionadas