Comment exprimer correctement JPQL " join fetch "avec la clause" where " comme critère JPA 2?

considère la requête JPQL suivante:

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

j'essaie de traduire ceci en une requête Critieria. C'est tout ce que j'ai obtenu:

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

le problème évident ici est que je fais la même jointure deux fois, parce que Fetch<Foo, Bar> ne semble pas avoir une méthode pour obtenir un Path . Est-il un moyen pour éviter d'avoir à se joindre à deux fois? Ou dois-je m'en tenir au bon vieux JPQL avec une requête aussi simple que celle-là?

39
demandé sur Sean Patrick Floyd 2011-04-28 13:23:47

2 réponses

En JPQL le même est vrai dans la spec. La spécification JPA ne permet pas de donner un alias à une jointure fetch. Le problème est que vous pouvez facilement tirer vous-même dans le pied avec cela en limitant le contexte du fetch join. Il est plus sûr de se joindre deux fois.

c'est normalement plus un problème avec ToMany qu'avec ToOnes. Par exemple,

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

ce sera incorrectement retourner tous les employés qui contiennent numéros dans l'indicatif régional "613", mais les numéros de téléphone des autres régions de la liste retournée seront omis. Cela signifie qu'un employé qui avait un téléphone dans les indicatifs régionaux 613 et 416 perdra le numéro de Téléphone 416, donc l'objet sera corrompu.

accordé, si vous savez ce que vous faites, la jointure supplémentaire n'est pas souhaitable, certains fournisseurs JPA peuvent autoriser l'aliasing le fetch de jointure, et peut autoriser le moulage des critères Fetch à une jointure.

59
répondu James 2018-02-22 02:45:57

je vais montrer visuellement le problème, en utilisant le grand exemple de la réponse de James et en ajoutant la solution alternative.

lorsque vous faites la requête suivante, sans le FETCH :

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

vous aurez les résultats suivants de Employee comme vous vous y attendiez:

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

mais quand vous ajoutez le FETCH mot sur JOIN , c'est ce qui se passe:

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

le le SQL généré est le même pour les deux requêtes, mais L'hibernation supprime sur la mémoire le 416 enregistrer lorsque vous utilisez WHERE sur le FETCH joindre.

donc, pour apporter tous les téléphones et appliquer le WHERE correctement, vous devez avoir deux JOIN s: un pour le WHERE et un autre pour le FETCH . Comme:

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
répondu Dherik 2018-09-19 20:56:07