Open Session In View Pattern
El problema que OSIV resuelve es un desajuste entre la sesión de hibernate y su ciclo de vida y la forma en la que trabajan las tecnologías de vista del lado servidor.
En una aplicación típica de java la capa de servicio hace queries con datos para construir la vista. Sin embargo habrá datos que no sean cargados hasta que sean necesarios (lazy loaded) con la condición que haya una sesión abierta, y allí es donde empieza el problema.
Entre el momento en que la capa de servicio termina su ejecución y el momento en que la vista es renderizada, hibernate ya ha hecho commit de la tx y cerrado la sesión. Cuando la vista intenta cargar los datos que no fueron cargados en el query (lazy load), encuentra que la sesión ha sido cerrada causando LazyInitializationException
.
OSIV resuelve este problema asegurándose que la sesión de hibernate permanezca abierta hasta que se termine de renderizar la vista, y de ahí el nombre del patrón.
Gracias a que la sesión se mantiene abierta, ya no ocurren más LazyInitializationExceptions
. La sesión se mantiene abierta gracias a un filtro que es agregado al procesamiento del request.
En el caso de JPA el OpenEntityManagerInViewFilter
creará un entity manager (sesión de hibernate) al inicio del request y luego lo unirá al thread del request.
Luego la capa de servicio será ejecutada y las transacciones commited/rollback sin que el transaction manager borre la sesión del thread después de hacer commit/rollback.
Cuando comienza el renderizado de la vista, el transaction manager revisará si existe una sesión unida al thread del request, si existe la usuará en vez de crear una nueva.
Después de que el request es procesado, el filtro soltará a la sesión del thread.
El resultado final es que la misma sesión utilizada para hacer commit de las transacciones de la capa de servicio se mantiene abierta y agarrada al thread del request, permitiendo que el renderizado de la vista pueda cargar los objetos que se vayan necesitando (lazy loading).
El problema original son las LazyInitializationException
. Sin embargo esta excepción es una advertencia de que un query no está bien escrito en la capa de servicio.
Cuando se construye una vista, el desarrollador sabe a priory qué datos se necesitan y puede asegurarse que los datos que se necesitarán en la vista sean leidos por completo antes de que comience el renderizado.
Muchas relaciones por ejemplo one-to-many utilizan lazy loading por default, sin embargo el comportamiento puede ser reescrito en los queries utlizando la siguiente sintaxis:
select p FROM Person p left join fetch p.invoices
Esto significa que lazy loading puede ser apagado dependiendo del tipo de datos necesarios en la vista.
La principal ventaja de OSIV es que ayuda a trabajar con el ORM de una forma más transparente:
LazyInitializationExceptions
.OSIV es fácil de abusar y accidentalmente introducir el problema N+1
en la aplicación lo cual puede llevar a problemas de rendimiento.
La alternativa es utilizar queries que carguen explicitamente los datos que se vayan a utilizar en la vista dependiendo del caso de uso.
TODO: OSIVFIlter OSIVInterceptor?
The open session in view (OSIV) pattern is common when using Hibernate, and Grails implements it with the GrailsOpenSessionInViewInterceptor
class (which extends the org.spring framework.orm.hibernate3.support.OpenSessionInViewInterceptor
Spring class). This interceptor opens a Hibernate session at the beginning of each request, and flushes and closes it after it’s finished.
This is primarily there for lazy-loaded one-to-many collections and many-to-one relationships. If there wasn’t an open session, after loading the instance, it would immediately become disconnected. This is because GORM uses an org.springframe work.orm.hibernate3.HibernateTemplate to do the querying and it has logic to use an existing session using SessionFactoryUtils.getSession() or create one if none is active. When it gets an active session, it doesn’t close it because it didn’t open it, but if
it has to create one, then it will close it. This disconnects all loaded instances, so trying to access a collection would throw an exception. Because the OpenSessionInViewInterceptor
opens a session and registers it in a ThreadLocal (via SessionFactoryUtils
), this keeps the session active, and the collections and relationships can be resolved.
http://blog.jhades.org/open-session-in-view-pattern-pros-and-cons/