java - one - Ignorar un FetchType.EAGER en una relación
hibernate one to many unidirectional xml mapping example (5)
- Sí, puede asignar dos clases de entidad a la misma tabla y esa es una solución válida. Sin embargo, tenga cuidado con las situaciones en las que las instancias de ambos tipos están presentes simultáneamente en el mismo contexto de persistencia, porque las actualizaciones de una instancia de entidad de un tipo no se reflejan en la misma instancia del otro tipo. Además, el almacenamiento en caché de segundo nivel de tales entidades se vuelve más complicado.
- Los perfiles de captación también son interesantes, pero en este momento son muy limitados y puede anular el plan / estrategia de captación por defecto solo con los perfiles de captación de estilo de unión (puede hacer que una asociación perezosa sea entusiasta, pero no al revés). Sin embargo, podría usar este trick para invertir ese comportamiento: haga que la asociación sea perezosa de forma predeterminada y habilite el perfil de forma predeterminada para todas las sesiones / transacciones. Luego, deshabilite el perfil en las transacciones en las que desea una carga lenta.
Tengo un problema con las relaciones de EAGER en una aplicación grande. Algunas entidades en esta aplicación tienen asociaciones EAGER
con otras entidades. Esto se convierte en "veneno" en algunas funcionalidades.
Ahora mi equipo necesita optimizar estas funcionalidades, pero no podemos cambiar el tipo de recuperación a LAZY , porque tendríamos que refactorizar toda la aplicación.
Entonces, mi pregunta: ¿hay una manera de hacer una consulta específica ignorando las asociaciones de EAGER en mi entidad devuelta?
Ejemplo: cuando tengo una persona de esta entidad, me gustaría no traer la lista de direcciones cuando hago una consulta para encontrar una persona.
@Entity
public class Person {
@Column
private String name;
@OneToMany(fetch=FetchType.EAGER)
private List<String> address;
}
Query query = EntityManager.createQuery("FROM Person person");
//list of person without the address list! But how???
List<Person> resultList = query.getResultList();
¡Gracias!
Actualizado
La única forma que encontré es no devolver la entidad, devolviendo solo algunos campos de la entidad. Pero me gustaría encontrar una solución que pueda devolver a la entidad (en mi ejemplo, la entidad Person
).
Estoy pensando si es posible mapear la misma tabla dos veces en Hibernate. De esta manera, puedo mapear la misma tabla sin las asociaciones EAGER. Esto me ayudará en algunos casos ...
De forma predeterminada, HQL, Criteria y NativeSQL de Hibernate nos dan la flexibilidad de cargar una colección de EAGERly si se asigna 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 y tratar de hacer una carga LAZY usando HQL, Criteria o NativeSQL, no pude encontrar una manera simple o sencilla de encontrar esto. HQL / Criterios / NativeSQL.
Aunque tenemos FetchMode.LAZY
que podemos establecer en Criterios, está en desuso y es equivalente a FetchMode.SELECT
. Y efectivamente, FetchMode.LAZY en realidad genera una consulta SELECT adicional y todavía carga la colección con entusiasmo.
Pero, si queremos cargar LAZY una colección 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 objeto de entidad (o DTO) con campos rellenados a partir de valores escalares.
En mi escenario, tengo una entidad de bosque que tiene una colección de entidades de árbol con mapeo FetchType.EAGER
de FetchType.EAGER
y FetchMode.JOIN
. Para cargar solo la entidad Forest sin cargar ningún árbol, he usado la siguiente consulta HQL con valores escalares y Transformers.aliasToBean (...) . Esto funciona con Criteria y SQL nativo, 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 comprobado la consulta anterior y podría estar funcionando comprobando si esto funciona también en casos complejos y se ajusta a todos sus casos de uso.
Quisiera saber si existe una forma mejor o más sencilla de hacerlo, especialmente sin escalas y transformadores.
No dijiste por qué no podías cambiar de ansioso a perezoso. Sin embargo, tengo la impresión de que fue por razones de rendimiento, por lo que quiero cuestionar esta suposición. Si ese es el caso, considere lo siguiente. Me doy cuenta de que esta respuesta no responde estrictamente a su pregunta y viola la condición de que no haya cargas perezosas, pero aquí hay una alternativa que refleja el enfoque de mi equipo de desarrollo respecto a lo que creo que es el mismo problema subyacente.
Establezca el tipo de recuperación como perezoso, pero luego establezca una anotación @BatchSize. Como hibernate generalmente usa una consulta de base de datos separada para cargar una colección, esto mantiene ese comportamiento pero con un BatchSize sintonizado, evita una consulta por elemento (mientras está en un bucle, por ejemplo), siempre que su sesión aún esté abierta.
El comportamiento para el retroceso de una relación OneToOne se vuelve un poco raro (el lado de referencia de la relación - el lado sin la clave externa). Pero para el otro lado de OneToOne, para OneToMany y ManyToOne, esto da el resultado final que creo que probablemente desee: solo consulta las tablas si realmente las necesita, pero evita una carga lenta por registro, y no lo hace. Hay que configurar explícitamente cada caso de uso. Esto significa que su rendimiento será comparable en el caso de que ejecute la carga diferida, pero esta carga no se producirá si realmente no la necesita.
Nunca intenté esto realmente, pero podría valer la pena intentarlo ... Suponiendo que la fábrica de sesiones está disponible en la capa DAO mediante inyección o cualquier otro medio, puede implementar algo similar en un método DAO (probablemente nuevo):
List<Person> result = (List<Person>) sessionFactory.getCurrentSession()
.createCriteria(Person.class)
.setFetchMode("address", FetchMode.LAZY)
.list();
return result;
Si está utilizando JPA 2.1 (Hibernate 4.3+), puede lograr lo que quiere con @NamedEntityGraph.
Básicamente, usted anotaría su entidad de esta manera:
@Entity
@NamedEntityGraph(name = "Persons.noAddress")
public class Person {
@Column
private String name;
@OneToMany(fetch=FetchType.EAGER)
private List<String> address;
}
Y luego usa los consejos para obtener Persona sin dirección, como esto:
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 here .
Cuando use el gráfico de recuperación, solo los campos que haya puesto en @NamedEntityGraph se buscarán con entusiasmo.
Todas las consultas existentes que se ejecuten sin la sugerencia seguirán siendo las mismas.