java - recursive - Resuelve el problema de Hibernate Lazy-Init con hibernate.enable_lazy_load_no_trans
hibernate lazy example (4)
He estado sufriendo de una infame excepción de hibernación
org.hibernate.LazyInitializationException: could not initialize proxy - no Session
Ahora la comunidad está animando
<property name="hibernate.enable_lazy_load_no_trans" value="true"/>
diciendo que resuelve el problema pero ÚSELO CON PRECAUCIÓN .
¿Qué quieren decir con usarlo con precaución? Lo que esta propiedad realmente hace?
Por favor, dame algunas ideas. Gracias por adelantado.
El problema con este enfoque es que puedes tener el efecto N + 1.
Imagina que tienes la siguiente entidad:
public class Person{
@OneToMany // default to lazy
private List<Order> orderList;
}
Si tiene un informe que devuelve 10K de personas, y si en este informe ejecuta el código person.getOrderList()
el JPA / Hibernate ejecutará 10K de consultas. Este es el efecto N + 1, no tendrá control sobre todas las consultas que se ejecutarán.
Imagine ahora que el orden es como a continuación:
public class Order{
@OneToMany // default to lazy
private List<EmailSent> emailSentList;
}
Imagine ahora que tiene una iteración con la person.getOrderList()
y para cada Order order
hará un order.getEmailSentList()
. ¿Puedes ver el problema ahora?
Para LazyInitializationException puede tener algunas soluciones:
- Use el enfoque OpenInSessionInView. Tendrá que crear un WebFilter que abrirá y cerrará la transacción. El problema con es el efecto N + 1.
- Utilice la configuración hibernate.enable_lazy_load_no_trans, que es una hibernación y no podrá transferir su proyecto a otro proveedor de JPA si es necesario. También puede tener el efecto N + 1.
- Use la característica EJB llamada PersistenceContext Extended. Con esto mantendrás el contexto abierto de varias transacciones. Los problemas son: puede haber un efecto N + 1, usar mucha memoria del servidor (las entidades se mantendrán administradas)
- Use el FETCH en la consulta. Con este enfoque, podría hacer un JPQL / HQL como:
select p from Person p join fetch p.orderList
. Con esta consulta tendrá su lista cargada desde la base de datos y no tendrá el efecto N + 1. El problema es que necesitará escribir un JPQL para cada caso.
Si aún tiene algún problema, consulte estos enlaces:
Esto va en contra de cómo podemos aprovechar la aplicación de Hibernate de la semántica de lectura repetible con el concepto de sesión. Cuando un objeto se carga por primera vez y se hace referencia al objeto nuevamente dentro de la duración de la sesión, se devuelve el mismo objeto INDIFERENTE de si este objeto ha cambiado en la base de datos. Esta es la semántica de lectura repetible proporcionada automáticamente por hibernación.
Con esta configuración, no tiene sesión que brinde esta garantía, por lo que si ahora accede a esta información obtendrá la última versión de los datos.
Esto podría estar bien. Pero considere el escenario donde este objeto se mantiene en algún lugar durante mucho tiempo y los datos han cambiado considerablemente, por lo que los datos obtenidos de forma diferida son muy diferentes a los datos ya cargados cuando la sesión estaba activa. Esto es por lo que debes preocuparte.
Para simplificar, puede usar esta configuración de manera segura si su programa no se ve afectado por: Cómo se estancaron los datos que ya se obtuvieron durante la sesión a los datos que se extraerán perezosamente de la sesión.
Pero si esto ( su programa está expuesto a problemas de sincronización, cuando podría funcionar bien una vez y fallar en otro momento ) es una preocupación, luego busque todos los datos necesarios mientras está en sesión.
Probablemente porque hay mejores soluciones, como @Transactional , donde las sesiones de apertura y cierre siguen un patrón muy común de "abrir una sesión y luego envolver todo en un try-catch-finally; catch vuelve atrás y finalmente cierra la sesión". Esta anotación suele ser a nivel de solicitud para aplicaciones y servicios web.
O si necesita un control más granular, puede abrir sesiones manualmente utilizando una SessionFactory.
Y como otros han mencionado, la carga lenta es algo que debes tener en cuenta. No es una bala de plata, pero puede ser muy útil. En general, si sus aplicaciones están diseñadas para tener muchas solicitudes pequeñas, entonces está bien.
La carga ansiosa también puede ser muy mala. Por ejemplo, cuando su modelo de objetos tiene muchas relaciones entre varios, pero sus solicitudes no usan datos de más de un nivel de profundidad.
O simplemente puedes olvidar todo por ahora. Use la carga diferida hasta que se convierta en un problema. Y si lo hace, hubieras sido mejor con Mybatis de todos modos.
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 .