¿Y qué tipo de estrategias alternativas utiliza para evitar las excepciones LazyLoadExceptions?
Entiendo que la sesión abierta a la vista tiene problemas con:
- Aplicaciones en capas que se ejecutan en diferentes JVM
- Las transacciones se confirman solo al final, y lo más probable es que le gusten los resultados antes.
Pero, si sabe que su aplicación se ejecuta en una sola máquina virtual, ¿por qué no aliviar su dolor utilizando una estrategia de sesión abierta en vista?
java
hibernate
jpa
lazy-loading
open-session-in-view
HeDinges
fuente
fuente
Respuestas:
Porque enviar Proxies posiblemente no inicializados, especialmente colecciones, en la capa de vista y activar la carga de hibernación desde allí puede ser problemático tanto desde el punto de vista del rendimiento como de la comprensión.
Comprensión :
El uso de OSIV 'contamina' la capa de visualización con preocupaciones relacionadas con la capa de acceso a datos.
La capa de vista no está preparada para manejar lo
HibernateException
que puede suceder cuando se realiza una carga diferida, pero presumiblemente la capa de acceso a datos sí lo está.Rendimiento :
OSIV tiende a tirar de la carga de entidades adecuada debajo de la alfombra: tiende a no notar que sus colecciones o entidades se inicializan de manera perezosa (tal vez N + 1). Más conveniencia, menos control.
Actualización: consulte El antipatrón OpenSessionInView para una discusión más amplia sobre este tema. El autor enumera tres puntos importantes:
fuente
Para obtener una descripción más detallada, puede leer mi Antipatrón en vista de sesión abierta . De lo contrario, aquí hay un resumen de por qué no debería usar Open Session In View.
Open Session In View adopta un enfoque inadecuado para la obtención de datos. En lugar de dejar que la capa empresarial decida cómo es mejor obtener todas las asociaciones que necesita la capa de vista, obliga al contexto de persistencia a permanecer abierto para que la capa de vista pueda activar la inicialización del proxy.
OpenSessionInViewFilter
llama alopenSession
método del subyacenteSessionFactory
y obtiene un nuevoSession
.Session
está ligado alTransactionSynchronizationManager
.OpenSessionInViewFilter
llamadasdoFilter
de lajavax.servlet.FilterChain
referencia objeto y la solicitud se procesan más.DispatcherServlet
llama y enruta la solicitud HTTP al subyacentePostController
.PostController
llama aPostService
para obtener una lista dePost
entidades.PostService
abre una nueva transacción, y laHibernateTransactionManager
vuelve a utilizar el mismoSession
que fue inaugurado por elOpenSessionInViewFilter
.PostDAO
Obtiene la lista dePost
entidades sin inicializar cualquier forma de asociación perezoso.PostService
confirma la transacción subyacente, pero elSession
no está cerrada, ya que se abrió el exterior.DispatcherServlet
aperturas de representación de la interfaz de usuario, lo que, a su vez, navega por las asociaciones perezosos y desencadena su inicialización.OpenSessionInViewFilter
puede cerrar elSession
, y la conexión de base de datos subyacente se libera también.A primera vista, esto puede no parecer algo terrible, pero, una vez que lo ve desde la perspectiva de una base de datos, una serie de fallas comienzan a volverse más obvias.
La capa de servicio abre y cierra una transacción de base de datos, pero luego, no hay ninguna transacción explícita. Por esta razón, cada declaración adicional emitida desde la fase de representación de la interfaz de usuario se ejecuta en modo de confirmación automática. La confirmación automática ejerce presión sobre el servidor de la base de datos porque cada declaración debe vaciar el registro de transacciones en el disco, lo que provoca una gran cantidad de tráfico de E / S en el lado de la base de datos. Una optimización sería marcar
Connection
como de solo lectura, lo que permitiría al servidor de la base de datos evitar escribir en el registro de transacciones.Ya no hay separación de preocupaciones porque las declaraciones son generadas tanto por la capa de servicio como por el proceso de representación de la interfaz de usuario. Escribir pruebas de integración que afirmen la cantidad de declaraciones que se generan requiere pasar por todas las capas (web, servicio, DAO), mientras se implementa la aplicación en un contenedor web. Incluso cuando se utiliza una base de datos en memoria (por ejemplo, HSQLDB) y un servidor web ligero (por ejemplo, Jetty), estas pruebas de integración serán más lentas de ejecutar que si las capas estuvieran separadas y las pruebas de integración de back-end utilizaran la base de datos, mientras que Las pruebas de integración de front-end se burlaban de la capa de servicio por completo.
La capa de interfaz de usuario se limita a las asociaciones de navegación que, a su vez, pueden desencadenar problemas de consulta N + 1. Aunque Hibernate ofrece la
@BatchSize
posibilidad de obtener asociaciones en lotes yFetchMode.SUBSELECT
para hacer frente a este escenario, las anotaciones están afectando el plan de recuperación predeterminado, por lo que se aplican a cada caso de uso empresarial. Por esta razón, una consulta de la capa de acceso a datos es mucho más adecuada porque se puede adaptar a los requisitos de obtención de datos del caso de uso actual.Por último, pero no menos importante, la conexión de la base de datos podría mantenerse durante la fase de representación de la interfaz de usuario (según el modo de liberación de la conexión), lo que aumenta el tiempo de concesión de la conexión y limita el rendimiento general de las transacciones debido a la congestión en el grupo de conexiones de la base de datos. Cuanto más se mantenga la conexión, más solicitudes simultáneas esperarán para obtener una conexión del grupo.
Entonces, o mantiene la conexión durante demasiado tiempo, o adquiere / libera múltiples conexiones para una sola solicitud HTTP, lo que ejerce presión sobre el grupo de conexiones subyacentes y limita la escalabilidad.
Bota de primavera
Desafortunadamente, Open Session in View está habilitado de forma predeterminada en Spring Boot .
Por lo tanto, asegúrese de que en el
application.properties
archivo de configuración tenga la siguiente entrada:Esto desactivará OSIV, para que pueda manejar de la
LazyInitializationException
manera correcta .fuente
las transacciones se pueden realizar en la capa de servicio; las transacciones no están relacionadas con OSIV. Es el
Session
que permanece abierto, no una transacción, en ejecución.si las capas de su aplicación están distribuidas en varias máquinas, entonces prácticamente no puede usar OSIV; debe inicializar todo lo que necesita antes de enviar el objeto por el cable.
OSIV es una forma agradable y transparente (es decir, ninguno de su código es consciente de que sucede) de hacer uso de los beneficios de rendimiento de la carga diferida
fuente
No diría que Open Session In View se considera una mala práctica; que te da esa impresion?
Open-Session-In-View es un enfoque simple para manejar sesiones con Hibernate. Porque es simple, a veces es simplista. Si necesita un control detallado sobre sus transacciones, como tener múltiples transacciones en una solicitud, Open-Session-In-View no siempre es un buen enfoque.
Como han señalado otros, OSIV tiene algunas compensaciones: es mucho más propenso al problema N + 1 porque es menos probable que se dé cuenta de las transacciones que está iniciando. Al mismo tiempo, significa que no necesita cambiar su capa de servicio para adaptarse a cambios menores en su vista.
fuente
Si está utilizando un contenedor de Inversión de control (IoC) como Spring, es posible que desee leer sobre el alcance de los beans . Esencialmente, le estoy diciendo a Spring que me dé un
Session
objeto Hibernate cuyo ciclo de vida abarca toda la solicitud (es decir, se crea y se destruye al principio y al final de la solicitud HTTP). No tengo que preocuparme porLazyLoadException
cerrar la sesión, ya que el contenedor IoC lo gestiona por mí.Como se mencionó, tendrá que pensar en los problemas de rendimiento de N + 1 SELECT. Siempre puede configurar su entidad Hibernate después para realizar una carga de unión ansiosa en lugares donde el rendimiento es un problema.
La solución de alcance de beans no es específica de Spring. Sé que PicoContainer ofrece la misma capacidad y estoy seguro de que otros contenedores de IoC maduros ofrecen algo similar.
fuente
En mi propia experiencia, OSIV no es tan malo. El único arreglo que hice fue usar dos transacciones diferentes: - la primera, abierta en "capa de servicio", donde tengo la "lógica empresarial" - la segunda abierta justo antes de la representación de la vista
fuente
Acabo de publicar una publicación sobre algunas pautas sobre cuándo usar la sesión abierta a la vista en mi blog. Compruébalo si estás interesado.
http://heapdump.wordpress.com/2010/04/04/should-i-use-open-session-in-view/
fuente
Estoy muy oxidado con Hibernate ... pero creo que es posible tener múltiples transacciones en una sesión de Hibernate. Por lo tanto, los límites de su transacción no tienen que ser los mismos que los eventos de inicio / detención de sesión.
OSIV, en mi opinión, es principalmente útil porque podemos evitar escribir código para iniciar un 'contexto de persistencia' (también conocido como sesión) cada vez que la solicitud necesita hacer un acceso a la base de datos.
En su capa de servicio, probablemente necesitará realizar llamadas a métodos que tienen diferentes necesidades de transacción, como 'Requerido, Nuevo Requerido, etc.' Lo único que necesitan estos métodos es que alguien (es decir, el filtro OSIV) haya iniciado el contexto de persistencia, por lo que lo único que tienen que preocuparse es - "Oye, dame la sesión de hibernación para este hilo ... Necesito hacer algo Cosas de DB ".
fuente
Esto no ayudará demasiado, pero puede consultar mi tema aquí: * Hibernate Cache1 OutOfMemory con OpenSessionInView
Tengo algunos problemas de OutOfMemory debido a OpenSessionInView y muchas entidades cargadas, porque permanecen en el nivel de caché de Hibernate 1 y no se recolectan basura (cargo muchas entidades con 500 elementos por página, pero todas las entidades permanecen en la caché)
fuente