web-development-kb-es.site

Hibernate (JPA) cómo hacer una consulta ansiosa, cargando todos los objetos secundarios

En relación con mi pregunta anterior , quiero asegurarme de que todos los objetos secundarios estén cargados, ya que tengo varios subprocesos que pueden necesitar acceder a los datos (y así evitar excepciones de carga lenta). Entiendo que la forma de hacerlo es usar la palabra clave "fetch" en la consulta (EJB QL). Me gusta esto:

select distinct o from Order o left join fetch o.orderLines

Suponiendo un modelo con una clase Order que tiene un conjunto de OrderLines.

Mi pregunta es que la palabra clave "distinta" parece ser necesaria ya que de lo contrario parece que obtengo un Order por cada OrderLine. ¿Estoy haciendo lo correcto?

Quizás lo más importante, ¿hay alguna forma de atraer todos los objetos secundarios, sin importar cuán profundo sea? Tenemos alrededor de 10-15 clases y para el servidor necesitaremos todo cargado ... Estaba evitando usar FetchType.EAGER ya que eso significa que siempre está ansioso y, en particular, el front-end web carga todo, pero tal vez ese es el camino a seguir, ¿es eso lo que haces? Me parece recordar que intentamos esto antes y luego obtuvimos páginas web realmente lentas, pero ¿tal vez eso significa que deberíamos estar usando un caché de segundo nivel?

20
Chris Kimpton

Cambiar la anotación es una mala idea de la OMI. Como no se puede cambiar a vago en tiempo de ejecución. Es mejor hacer todo perezoso y buscarlo según sea necesario.

No estoy seguro de entender su problema sin mapeos. La búsqueda de unión izquierda debe ser todo lo que necesita para el caso de uso que describe. Por supuesto, recibirá un pedido por cada línea de pedido si la línea de pedido tiene un pedido como padre.

14
James Law

No estoy seguro de usar la palabra clave fetch en su EJBQL, podría confundirlo con la anotación ...

¿Has intentado agregar la propiedad FetchType a tu atributo de relación?

@OneToMany (fetch = FetchType.EAGER)?

Ver:

http://Java.Sun.com/javaee/5/docs/api/javax/persistence/FetchType.htmlhttp://www.jroller.com/eyallupu/entry/ hibernate_exception_simuallyly_fetch_multiple

8
Jeremy

¿Has intentado usar un transformador de resultados? Si usa consultas de Criterio, puede aplicar un transformador de resultados (aunque hay algunos problemas con la paginación y el transformador de resultados ):

Criteria c = ((Session)em.getDelegate()).createCriteria(Order.class);
c.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
c.list();

em.getDelegate() es un hack que solo funciona si estás usando hibernate.

Quizás lo más importante, ¿hay alguna forma de atraer todos los objetos secundarios, sin importar cuán profundo sea? Tenemos alrededor de 10-15 clases y para el servidor necesitaremos todo cargado ... Estaba evitando usar FetchType.EAGER, ya que eso significaba que siempre estaba ansioso y en particular el front-end web carga todo, pero tal vez ese es el camino a seguir. - ¿Es eso lo que haces? Me parece recordar que intentamos esto antes y luego obtuvimos páginas web realmente lentas, pero ¿tal vez eso significa que deberíamos estar usando un caché de segundo nivel?

Si todavía está interesado, respondí una pregunta similar en este hilo cómo serializar colecciones de hibernación .

Básicamente, utiliza una utilidad llamada dozer que asigna beans a otros beans, y al hacer esto, activa todas sus cargas diferidas. Como puede imaginar, esto funciona mejor si todas las colecciones se obtienen con entusiasmo.

3
Miguel Ping

Es posible que pueda hacer algo así utilizando una consulta de criterios (separada) y configurando el modo de recuperación. P.ej.,

Session s = ((HibernateEntityManager) em).getSession().getSessionFactory().openSession();
DetachedCriteria dc = DetachedCriteria.forClass(MyEntity.class).add(Expression.idEq(id));
dc.setFetchMode("innerTable", FetchMode.JOIN);
Criteria c = dc.getExecutableCriteria(s);
MyEntity a = (MyEntity)c.uniqueResult();
2
Mike Desjardins

Lo que he hecho es refactorizar el código para mantener un mapa de objetos para los administradores de entidades y cada vez que necesite actualizar, cierre el antiguo administrador de entidades para el objeto y abra uno nuevo. Utilicé la consulta anterior sin fetch ya que eso es demasiado profundo para mis necesidades, solo haciendo uniones simples en las líneas de pedido; fetch lo hace aún más profundo.

Hay solo unos pocos objetos para los que necesito esto, alrededor de 20, por lo que creo que la sobrecarga de recursos al tener 20 administradores de entidades abiertos no es un problema, aunque los DBA pueden tener una visión diferente cuando esto se active ...

También volví a trabajar las cosas para que el trabajo de db esté en el hilo principal y tenga el administrador de la entidad.

Chris

0
Chris Kimpton

Eso solo funcionaría para las relaciones ManyToOne y para ellos @ManyToOne (fetch = FetchType.EAGER) probablemente sería apropiado.

No se recomienda buscar más de una relación OneToMany con entusiasmo y/o no funciona, como puede leer en el enlace publicado por Jeremy. Solo piense en la declaración SQL que sería necesaria para hacer esa búsqueda ...

0
jrudolph