Saltar al contenido

Ignorar un FetchType.EAGER en una relación

Esta noticia ha sido analizado por expertos para que tengas la garantía de la veracidad de nuestro post.

Solución:

Si está utilizando JPA 2.1 (Hibernate 4.3+), puede lograr lo que desea con @NamedEntityGraph.

Básicamente, anotarías tu entidad así:

@Entity
@NamedEntityGraph(name = "Persons.noAddress")
public class Person 

  @Column
  private String name;

  @OneToMany(fetch=FetchType.EAGER)
  private List address;


Y luego use las sugerencias para obtener Persona sin dirección, así:

EntityGraph graph = this.em.getEntityGraph("Persons.noAddress");

Map hints = new HashMap();
hints.put("javax.persistence.fetchgraph", graph);

return this.em.findAll(Person.class, hints);

Más sobre el tema se puede encontrar aquí.

Cuando use el gráfico de búsqueda, solo los campos que haya colocado dentro de @NamedEntityGraph se buscarán con entusiasmo.

Todas las consultas existentes que se ejecutan sin la sugerencia seguirán siendo las mismas.

Actualizar (06/09/2020):

El problema se resolvió en la versión 5.4.11. No puedo probar en este momento, pero se espera que los gráficos de entidades JPA attributes no incluidos en el gráfico deben permanecer descargados, incluso si se declaran EAGER.

respuesta original

Después de todos estos años, anular el mapeo EAGER es aún no es posible en hibernación. De la última documentación de Hibernate (5.3.10.Final):

Aunque el estándar JPA especifica que puede anular una asociación de búsqueda EAGER en tiempo de ejecución usando la sugerencia javax.persistence.fetchgraph, actualmente, Hibernate no implementa esta función, por lo que las asociaciones EAGER no se pueden buscar perezosamente. Para obtener más información, consulte el problema de Jira HHH-8776.

Al ejecutar una consulta JPQL, si se omite una asociación EAGER, Hibernate emitirá una selección secundaria para cada asociación que se necesite obtener con entusiasmo, lo que puede generar problemas de consulta dto N+1.

Por esta razón, es mejor usar asociaciones LAZY y solo obtenerlas con entusiasmo por consulta.

Y:

La estrategia de búsqueda EAGER no se puede sobrescribir por consulta, por lo que la asociación siempre se recuperará incluso si no la necesita. Además, si olvida JOIN FETCH una asociación EAGER en una consulta JPQL, Hibernate la inicializará con una declaración secundaria, lo que a su vez puede generar problemas de consulta N+1.

De forma predeterminada, HQL, Criteria y NativeSQL de Hibernate nos brindan la flexibilidad de cargar una colección con EAGERly si está asignada como LAZY en el modelo de dominio.

Con respecto a lo contrario, es decir, mapear la colección como EAGER en el modelo de dominio e intentar hacer una carga LAZY usando HQL, Criteria o NativeSQL, no pude encontrar una manera directa o más simple en la que podamos resolver esto con HQL/Criterios/NativeSQL.

aunque tenemos FetchMode.LAZY que podemos establecer en Criteria, está en desuso y es equivalente a FetchMode.SELECT. Y, de hecho, FetchMode.LAZY da como resultado que se active una consulta SELECT adicional y aún carga la colección con entusiasmo.

Pero, si queremos cargar LAZY una colección que está asignada como EAGER, puede probar esta solución: haga que HQL/Criteria/NativeSQL devuelva los valores escalares y use un ResultTransformer(Transformers.aliasToBean(..)) para devolver el objeto de entidad (o DTO) con campos rellenados a partir de valores escalares.

En mi escenario, tengo un bosque entidad que tiene una colección de Árbol entidades con uno a muchos mapeo de FetchType.EAGER y FetchMode.JOIN. Para cargar solo el bosque entidad sin cargar ningún árbol, he usado la siguiente consulta HQL con valores escalares y Transformers.aliasToBean(…). Esto funciona con Criteria y Native SQL, así como siempre que se utilicen escalares y aliasToBean Transformer.

Forest forest = (Forest) session.createQuery("select f.id as id, f.name as name, f.version as version from Forest as f where f.id=:forest").setParameter("forest", i).setResultTransformer(Transformers.aliasToBean(Forest.class)).uniqueResult();

He probado la consulta simple anterior y podría estar funcionando comprobando si esto también funciona para casos complejos y se adapta a todos sus casos de uso.

Estaría interesado en saber si hay una forma mejor o más simple de hacerlo, especialmente sin escalares y transformadores.

Si crees que ha resultado provechoso nuestro post, agradeceríamos que lo compartas con otros desarrolladores y nos ayudes a extender nuestra información.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *