java - para - Al utilizar la API de criterios de JPA, ¿puede realizar una unión de recuperación que resulte en una sola unión?
jpa para dummies (3)
Utilizando JPA 2.0. Parece que de forma predeterminada (sin recuperación explícita), los @OneToOne(fetch = FetchType.EAGER)
se recuperan en las consultas 1 + N, donde N es el número de resultados que contienen una Entidad que define la relación con una entidad relacionada distinta. Usando la API de criterios, podría intentar evitarlo de la siguiente manera:
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));
Lo anterior debería ser idealmente equivalente a lo siguiente:
SELECT m FROM MyEntity m JOIN FETCH myEntity.relatedEntity r WHERE r.id = 3
Sin embargo, los resultados de la consulta de criterios en la tabla raíz se unen innecesariamente a la tabla de entidades relacionadas dos veces; una vez para la búsqueda, y una vez para el predicado donde. El SQL resultante se parece a esto:
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
Por desgracia, si solo hago la búsqueda, entonces no tengo una expresión para usar en la cláusula where.
¿Me estoy perdiendo algo, o esto es una limitación de la API de criterios? Si es lo último, ¿se está solucionando esto en JPA 2.1 o hay mejoras específicas del proveedor?
De lo contrario, parece mejor simplemente abandonar la verificación de tipos en tiempo de compilación (me doy cuenta de que mi ejemplo no usa el metamodelo) y usar JPQL TypedQueries dinámico.
A partir de JPA 2.1 puede usar gráficos de entidad dinámicos para esto. Elimine su búsqueda y especifique un gráfico de entidad de la siguiente manera:
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);
El uso de root.fetch()
en EclipseLink creará un SQL con INNER JOIN
porque tiene 3 tipos y el valor predeterminado es INNER.
INNER, LEFT, RIGHT;
La sugerencia es utilizar CreateQuery.
TypedQuery<T> typedQuery = entityManager.createQuery(query);
EDITAR: Puedes lanzar la raíz de De así:
From<?, ?> join = (From<?, ?>) root.fetch("relatedEntity");
En lugar de root.join(...)
puede usar root.fetch(...)
que devuelve el objeto Fetch<>
.
pero se puede utilizar de manera similar. Fetch<>
es descendiente de Join<>
Solo debes lanzar Fetch<>
para Join<>
que debería funcionar para EclipseLink e Hibernate
...
Join<MyEntity, RelatedEntity> join = (Join<MyEntity, RelatedEntity>)root.fetch("relatedEntity");
...