¿Cómo expresar correctamente JPQL " join fetch "con la cláusula" where " como JPA 2 CriteriaQuery?


Considere la siguiente consulta JPQL:

SELECT foo FROM Foo foo
INNER JOIN FETCH foo.bar bar
WHERE bar.baz = :baz

Estoy tratando de convertir esto en un Criterio de consulta. Esto es lo más lejos que he llegado:

CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Foo> cq = cb.createQuery(Foo.class);
Root<Foo> r = cq.from(Foo.class);
Fetch<Foo, Bar> fetch = r.fetch(Foo_.bar, JoinType.INNER);
Join<Foo, Bar> join = r.join(Foo_.bar, JoinType.INNER);
cq.where(cb.equal(join.get(Bar_.baz), value);

El problema obvio aquí es que estoy haciendo el mismo join dos veces, porque Fetch<Foo, Bar> no parece tener un método para obtener un Path. ¿Hay alguna manera de evitar tener que unirse dos veces? ¿O tengo que seguir con el viejo JPQL con una consulta tan simple como eso?

Author: Sean Patrick Floyd, 2011-04-28

2 answers

En JPQL lo mismo es realmente cierto en la especificación. La especificación de JPA no permite que se le dé un alias a una combinación fetch. El problema es que puede dispararse fácilmente en el pie con esto restringiendo el contexto de la búsqueda de unión. Es más seguro unirse dos veces.

Esto es normalmente más un problema con ToMany que ToOnes. Por ejemplo,

Select e from Employee e 
join fetch e.phones p 
where p.areaCode = '613'

Esto incorrectamente devolverá todos los empleados que contienen números en el código de área ' 613 ' pero omitirán números de teléfono de otras áreas en la lista devuelta. Esto significa que un empleado que tenía un teléfono en los códigos de área 613 y 416 perderá el número de teléfono 416, por lo que el objeto estará dañado.

Concedido, si usted sabe lo que está haciendo, la unión extra no es deseable, algunos proveedores de JPA pueden permitir aliasing la búsqueda de unión, y pueden permitir la conversión de la Búsqueda de Criterios a una Combinación.

 59
Author: James,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-02-22 02:45:57

Mostraré visualmente el problema, usando el gran ejemplo de James answer y agregando la solución alternativa.

Cuando haces la consulta follow, sin el FETCH:

Select e from Employee e 
join e.phones p 
where p.areaCode = '613'

Tendrás los siguientes resultados de Employee como esperabas:

EmployeeId | EmployeeName | PhoneId | PhoneAreaCode
1          | James        | 5       | 613
1          | James        | 6       | 416

Pero cuando agregas la palabra FETCH en JOIN, esto es lo que sucede:{[16]]}

EmployeeId | EmployeeName | PhoneId | PhoneAreaCode
1          | James        | 5       | 613

El SQL generado es el mismo para las dos consultas, pero el Hibernate elimina en la memoria el registro 416 cuando se usa WHERE en la combinación FETCH.

Por lo tanto, para traer todos los teléfonos y aplicar el WHEREcorrectamente, necesita tener dos JOIN s: uno para el WHERE y otro para el FETCH. Como:

Select e from Employee e 
join e.phones p 
join fetch e.phones      //no alias, to not commit the mistake
where p.areaCode = '613'
 4
Author: Dherik,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-09-19 20:56:07