Utilisation de JPA 2.0. Il semble que par défaut (pas de récupération explicite), les champs @OneToOne(fetch = FetchType.EAGER)
sont récupérés dans des requêtes 1 + N, où N est le nombre de résultats contenant une entité qui définit la relation avec une entité liée distincte. En utilisant l'API Criteria, je pourrais essayer d'éviter cela comme suit:
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery<MyEntity> query = builder.createQuery(MyEntity.class);
Root<MyEntity> root = query.from(MyEntity.class);
Join<MyEntity, RelatedEntity> join = root.join("relatedEntity");
root.fetch("relatedEntity");
query.select(root).where(builder.equals(join.get("id"), 3));
Idéalement, ce qui précède devrait être équivalent à ce qui suit:
SELECT m FROM MyEntity m JOIN FETCH myEntity.relatedEntity r WHERE r.id = 3
Cependant, la requête de critères entraîne la jointure inutile de la table racine à la table d'entités associée deux fois; une fois pour le fetch, et une fois pour le prédicat where. Le SQL résultant ressemble à ceci:
SELECT myentity.id, myentity.attribute, relatedentity2.id, relatedentity2.attribute
FROM my_entity myentity
INNER JOIN related_entity relatedentity1 ON myentity.related_id = relatedentity1.id
INNER JOIN related_entity relatedentity2 ON myentity.related_id = relatedentity2.id
WHERE relatedentity1.id = 3
Hélas, si je ne fais que la récupération, je n'ai pas d'expression à utiliser dans la clause where.
Suis-je en train de manquer quelque chose, ou s'agit-il d'une limitation de l'API Criteria? Si c'est le dernier, cela est-il corrigé dans JPA 2.1 ou y a-t-il des améliorations spécifiques au fournisseur?
Sinon, il semble préférable de simplement abandonner la vérification de type à la compilation (je me rends compte que mon exemple n'utilise pas le métamodèle) et d'utiliser des JPQ TypedQueries dynamiques.
Au lieu de root.join(...)
, vous pouvez utiliser root.fetch(...)
qui renvoie Fetch<>
objet.
mais il peut être utilisé de manière similaire.Fetch<>
est un descendant de Join<>
Vous avez juste besoin de lancer Fetch<>
à Join<>
cela devrait fonctionner pour EclipseLink et Hibernate
...
Join<MyEntity, RelatedEntity> join = (Join<MyEntity, RelatedEntity>)root.fetch("relatedEntity");
...
À partir de JPA 2.1, vous pouvez utiliser des graphiques d'entités dynamiques pour cela. Supprimez votre extraction et spécifiez un graphique d'entité comme suit:
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery<MyEntity> query = builder.createQuery(MyEntity.class);
Root<MyEntity> root = query.from(MyEntity.class);
Join<MyEntity, RelatedEntity> join = root.join("relatedEntity");
query.select(root).where(builder.equal(join.get("id"), 3));
EntityGraph<MyEntity> fetchGraph = entityManager.createEntityGraph(MyEntity.class);
fetchGraph.addSubgraph("relatedEntity");
entityManager.createQuery(query).setHint("javax.persistence.loadgraph", fetchGraph);
L'utilisation de root.fetch()
sur EclipseLink créera un SQL avec INNER JOIN
car il existe 3 types et la valeur par défaut est INNER.
INNER, LEFT, RIGHT;
La suggestion est d'utiliser CreateQuery.
TypedQuery<T> typedQuery = entityManager.createQuery(query);
EDIT: vous pouvez convertir la racine en De comme ceci:
From<?, ?> join = (From<?, ?>) root.fetch("relatedEntity");