2009-02-23 21 views
47

Estoy trabajando en un proyecto para un cliente que quiere usar la inicialización perezosa. Siempre obtienen una "excepción de inicialización diferida" al asignar clases con el modo de carga diferida predeterminado.Cómo resolver la LazyInitializationException al usar JPA e Hibernate

@JoinTable(name = "join_profilo_funzionalita", joinColumns = {@JoinColumn(name = "profilo_id", referencedColumnName = "profilo_id")}, inverseJoinColumns = {@JoinColumn(name = "funzionalita_id", referencedColumnName = "funzionalita_id")}) 
//@ManyToMany(fetch=FetchType.EAGER) - no exceptions if uncommented 
@ManyToMany 
private Collection<Funzionalita> funzionalitaIdCollection; 

¿Existe un patrón estándar que utilice clases JPA para evitar este error?

Los fragmentos son bienvenidos, muchas gracias por su tiempo.

Respuesta

5

OpenSessionInView es un patrón para resolver este problema. Alguna información aquí:

http://www.hibernate.org/43.html

Usted querrá tener cuidado en la aplicación de este patrón y comprender las implicaciones. Cada vez que navega por una asociación perezosa en la vista, se activará otra consulta SQL para cargar los datos. Si sus casos de uso son tales que el número y tamaño de estas consultas SQL es pequeño, entonces esto puede no importar. Asegúrese de que, como mínimo, ajuste su configuración de registro para que pueda ver qué tipo de consultas Hibernate está ejecutando "mágicamente" en el fondo para que pueda cargar los datos.

Considera también el tipo de aplicación que estás escribiendo. Si no se trata de comunicación remota (no hay servicios web, no hay un cliente web basado en AJAX), OSIV puede funcionar muy bien. Sin embargo, si un serializador remoto comienza a recorrer el gráfico completo del objeto, es probable que desencadene una cantidad ridícula de consultas SQL y paralice su base de datos y el servidor de la aplicación.

+0

No utilice la sesión abierta a la vista si espera cargar en su servidor. Esta respuesta podría usar una advertencia en esa dirección. – iwein

+1

Pensé que el enlace sería suficiente para explicar las implicaciones del patrón, pero he agregado algunas advertencias por las dudas. :) –

7

LazyInitializationException significa que está llamando a la colección después de que se cerró la sesión de hibernación, o después de que el objeto se haya separado de la sesión.

Necesita volver a conectar el objeto a la sesión de hibernación, cambiar el lugar al que está llamando a la colección o mover el límite de donde la sesión se cierra a una capa más alta.

+12

que está bien pero ¿cómo? –

+2

Exactamente, tenemos la parte QUÉ; ¿CÓMO es la pregunta? – Nikhil

16

Hay muchas maneras de precargar propiedades, por lo que son allí después de la sesión es cerrada:

  1. llamada captador apropiado. Después de que el campo se busca en bean, está allí después de cerrar la sesión.
  2. Puede inicializar el campo en la consulta EJBQL, busque la palabra clave JOIN FETCH.
  3. Habilite AvailableSettings.ENABLE_LAZY_LOAD_NO_TRANS si tiene una versión de Hibernate que lo admita.

Varios problemas pueden producirse cuando intenta estas soluciones: invocación

  1. Los captadores puede ser optimizado de distancia por el compilador JIT (a veces esto lleva un tiempo).
  2. Las entidades que está tratando de JOIN FETCH pueden estar vinculadas a través de múltiples 'muchas' relaciones que implican la lista. En este caso, la consulta resultante arroja resultados ambiguos e Hibernate se negará a recuperar sus datos en una sola consulta.
  3. Ya existe una interesting bug related to AvailableSettings.ENABLE_LAZY_LOAD_NO_TRANS. Y habrá más porque, como dicen los chicos de hibernación: Nota: esto puede suceder fuera de la transacción y no es seguro. Usar con precaución. Eres mayormente solo.

La mejor manera de hacerlo es probar primero con JOIN FETCH. Si eso no funciona, intente con el enfoque getter. Si el compilador JIT se equivoca en el tiempo de ejecución, asigne el resultado a public static volatile Object.

o dejar de usar Hibernate ...

+0

Para el punto (3), vale la pena señalar que el error vinculado está cerrado a partir de la versión 4.1.7. En el momento de la redacción todavía hay un problema abierto relacionado aquí: https: //hibernate.atlassian.net/browse/HHH-8782. –

+2

"O deja de usar Hibernate ..." ... amén. –

4

Cuando se utiliza la recogida y desea inicializar con carga diferida luego usar esa colección antes de cierre de la sesión. Si la sesión está próxima después de eso, si quieres usar, obtienes lazyinitializeException porque lazy is try de forma predeterminada.

60

Hibernate 4.1.6 finalmente resuelve este problema: https://hibernate.atlassian.net/browse/HHH-7457

Es necesario configurar los hibernate.enable_lazy_load_no_trans de hibernación de propiedad = true

es como se hace en la primavera aquí:

<bean id="entityManagerFactory" 
     class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"> 
    <property name="dataSource" ref="myDataSource"/> 
    <property name="packagesToScan" value="com.mycompany.somepackage"/> 
    <property name="jpaVendorAdapter" ref="hibernateVendorAdapter"/> 
    <property name="jpaDialect" ref="jpaDialect"/> 
    <property name="jpaProperties"> 
     <props> 
      <prop key="hibernate.enable_lazy_load_no_trans">true</prop> 
     </props> 
    </property> 
</bean> 

Voila; Ahora no tiene que preocuparse por LazyInitializationException mientras navega por su dominio-modelo fuera de una sesión de hibernación (persistencia-contexto en "JPA-speak")

+3

¿Estaba contento de ver esto? muchas gracias. – Oversteer

+1

¿Podría agregar a su respuesta qué hace exactamente esta propiedad? La implementación puede tener serias implicaciones para la consistencia o el rendimiento de los datos. Probablemente ambos. – iwein

+1

Esta opción carga la asociación diferida fuera de la transacción, lo que significa que los datos pueden no ser consistentes con los de la entidad padre. Pero eso no es peor que usar Spring's OpenEntityManagerInViewFilter, que también carga cosas fuera de una transacción. Estoy usando un mapeo basado en el campo y no he experimentado que JIT estropee los proxies generados. – andreak

14

Tenga en cuenta que no debe usar hibernate.enable_lazy_load_no_trans pre Hibernate 4.1.7 , ya que pierde conexiones. Ver https://hibernate.onjira.com/browse/HHH-7524

+1

funciona, pero ahora tengo un problema de ciclo recursivo con el mapeo de hibernación –

1

Los tutoriales de Oracle Java señalan que "Enterprise beans admite transacciones, los mecanismos que administran el acceso concurrente de objetos compartidos". Entonces, para manejar los problemas de Lazy Fetch, creo un Bean de sesión Java Stateless y luego obtengo todas las subclases que necesito antes de regresar del método. Esto evita la excepción de búsqueda lenta. Oracle también se ha referido a esto como un patrón básico J2EE de "Fachada de Sesión". Este patrón parece mejor que algunas de las otras prácticas mencionadas.

2

The best way to solve the LazyInitializationException es utilizar la directiva JOIN FETCH en las consultas de su entidad.

EAGER loading es malo para el rendimiento. Además, hay anti-patrones tales como:

cual nunca se debe utilizar, ya que o bien requieren la conexión de base de datos para ser abierta para la prestación de interfaz de usuario (sesión abierta en Ver), o se necesita una conexión de base de datos para cada asociación diferida que se extrae fuera del Contexto de persistencia inicial (hibernate.enable_lazy_load_no_trans).

A veces, ni siquiera necesita entidades, y una proyección DTO es aún mejor. Debe buscar entidades solo cuando necesite modificarlas. Para transacciones de solo lectura, DTO projections are better.

Cuestiones relacionadas