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à?
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.
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'