java - org - Cómo resolver la LazyInitializationException al usar JPA e Hibernate
method threw org hibernate lazyinitializationexception exception cannot evaluate tostring() (9)
Cuando está utilizando la colección y desea inicializarla con una carga diferida, utilice esa colección antes de cerrar la sesión. Si la sesión está próxima después de eso, si quieres usar, obtienes lazyinitializeException porque lazy es intentar de forma predeterminada.
Estoy trabajando en un proyecto para un cliente que quiere usar la inicialización perezosa. Siempre obtienen "excepción de inicialización diferida" cuando asignan clases con el modo de carga diferida predeterminado.
@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;
¿Hay un patrón estándar usando clases JPA para evitar este error?
Los fragmentos son bienvenidos, muchas gracias por su tiempo.
Estoy trabajando en un proyecto que tiene como objetivo resolver problemas comunes de JPA al mapear entidades a DTO usando ModelMapper. Este problema ya ha sido resuelto en el proyecto. Enlace del proyecto: JPA Model Mapper
"Es crucial para el rendimiento declarar entidades como carga lenta, por lo que no necesitamos buscar todas las entidades relacionadas cada vez que necesitamos algunos datos. Pero esta técnica genera algunos problemas. La más común es la excepción LazyInitializationException que a veces puede ser bastante molesta. La mayoría de las veces solo queremos un objeto nulo para una entidad no cargada en lugar de un objeto que arroja una excepción si se accede ... "
Fuente: JPA Model Mapper
Por lo tanto, en el proyecto tratamos con LazyInitializationException estableciendo null para todas las entidades no cargadas. Los siguientes ejemplos muestran cómo funciona.
Reasignación de una configuración de entidad nula para todas las entidades no cargadas:
TypedQuery<SystemEntity> query =
em.createQuery("select s from SystemEntity s where s.id = 1", SystemEntity.class);
SystemEntity system = query.getSingleResult();
return new JpaModelMapper(em).mapEntity(system, SystemEntity.class);
Reasignación de una entidad a una configuración de DTO nula para todas las entidades no cargadas:
TypedQuery<SystemEntity> query =
em.createQuery("select s from SystemEntity s where s.id = 1", SystemEntity.class);
SystemEntity system = query.getSingleResult();
return new JpaModelMapper(em).mapEntity(system, SystemDTO.class);
Para obtener más información, consulte JPA Model Mapper
Hay muchas maneras de prearchivar las propiedades, por lo que están allí después de cerrar la sesión:
- Llame al getter apropiado. Después de que el campo se busca en bean, está allí después de cerrar la sesión.
- Puede inicializar el campo en la consulta EJBQL, busque la palabra clave
JOIN FETCH
. - Habilite AvailableSettings.ENABLE_LAZY_LOAD_NO_TRANS si tiene una versión de Hibernate que lo admita.
Varios problemas pueden ocurrir cuando prueba estas soluciones:
- La invocación de los getters puede ser optimizada por el compilador JIT (a veces esto lleva un tiempo).
- Las entidades a las que intenta unirse a
JOIN FETCH
pueden estar vinculadas a través de múltiples ''muchas'' relaciones que involucran a las de List. En este caso, la consulta resultante arroja resultados ambiguos e Hibernate se negará a recuperar sus datos en una sola consulta. - Ya hay un error interesante relacionado con AvailableSettings.ENABLE_LAZY_LOAD_NO_TRANS . Y habrá más porque, como dicen los chicos que están hibernando: Nota: esto puede suceder fuera de la transacción y no es seguro. Usar con precaución. Estás solo principalmente.
La mejor manera de hacerlo es intentar un JOIN FETCH
primero. Si eso no funciona, intente con el enfoque getter. Si el compilador JIT lo desordena en tiempo de ejecución, asigne el resultado a un public static volatile Object
.
O deja de usar Hibernate ...
Hibernate 4.1.6 finalmente resuelve este problema: https://hibernate.atlassian.net/browse/HHH-7457
Debe configurar la propiedad de hibernación hibernate.enable_lazy_load_no_trans = true
He aquí cómo hacerlo en primavera:
<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 su modelo de dominio fuera de una sesión de hibernación (persistencia-contexto en "JPA-speak")
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.
Debe volver a adjuntar 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.
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 central J2EE de "Fachada de Sesión". Este patrón parece mejor que algunas de las otras prácticas mencionadas.
OpenSessionInView es un patrón para resolver este problema. Alguna información aquí:
http://www.hibernate.org/43.html
Deberá tener cuidado al implementar 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.
También considere el tipo de aplicación que está 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.
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
La mejor forma de resolver la LazyInitializationException es utilizar la directiva JOIN FETCH en las consultas de su entidad.
La carga de EAGER es mala para el rendimiento. Además, hay antipatrones como:
Que nunca debe usar ya que requieren que la conexión de la base de datos esté abierta para la representación de la UI (Abrir sesión en la vista), o se necesita una conexión de base de datos para cada asociación diferida que se obtiene fuera del contexto de persistencia inicial ( hibernate.enable_lazy_load_no_trans
) .
A veces, ni siquiera necesita entidades, y una proyección de DTO es aún mejor. Deberías buscar entidades solo cuando necesites modificarlas. Para las transacciones de solo lectura, las proyecciones de DTO son mejores .