El patron de diseño "open session in view"

Hola a todos. Quien no se a encontrado con la siguiente excepcion usando Hibernate?:
org.hibernate.LazyInitializationException: could not initialize proxy - no Session

Normalmente sucede cuando se trabaja con Spring o cuando sencillamente queremos consultar los datos de una tabla que esta referenciada en otra (many-to-one), o de una manera mas clara sucede como la explica Ezamudio: El problema es porque lees un objeto de una entidad que tiene relación a muchos con otra entidad, el típico maestro-detalle, pero solamente leíste el maestro, que se queda con un falso arreglo en la relación al detalle, que sirve para que una Session de hibernate pueda sustituirlo por un arreglo ya con los detalles. Pero seguramente tienes un DAO o algo similar que lee el objeto, cierra la sesión, lo devuelve, y a la hora que lo quieres desplegar en tu JSP te sale esa excepción porque se intenta resolver el arreglo que nada más está de placeholder y no se puede porque no hay una sesión abierta.

Ok, esto se puede resolver de muchas manera, una es usando anotaciones EJB en tus beans poniendo lazy=false en la anotación de @Proxy de esta manera te ahorras los archivos .hbm.xml pero puedes incurrir en problemas de performances por que el maestro se trae todos los detalles asi sean miles o cientos y los mantiene en memoria. Otra forma es que tu archivos .hbm especifiquen en el atributo fetch="join" en la etiqueta y que en tus hql donde quieras conocer lo detalles del maestro lo especifiques de esta manera:
from Maestro as maestro left join fetch maestro.detalle, pero nos vamos a concentar en solucionarlo usando el patron "open session in view", el cual me han recomendado, si alguien encuentra algun error ya sea de logica, sintaxis, o de cualquier indole favor de avisarme para corregirlo.

Vamos a verlo usando un ejemplo sencillo con dos tablas (Profesor y Alumno), los beans serian los siguientes:

Bean Alumno
 

Bean Profesor:

 

Archivo de configuracion de hibernate "hibernate.cfg.xml"
 

Sevlet que implementa la interfaz Filter:
 

Archivo de configuracion "web.xml"
 

Clase con el metodo que realiza la consulta:
 

De esta forma podemos consultar de la siguiente manera sin problemas:

 

Opciones de visualización de comentarios

Seleccione la forma que prefiera para mostrar los comentarios y haga clic en «Guardar las opciones» para activar los cambios.
Imagen de ecamacho

gran post

Muy bueno javadicto, me gustó bastante. Sería bueno que incluyeras un war con el fuente y la aplicación corriendo para que lo tomen de ejemplo.

Hola ecamacho gracias por tu

Hola ecamacho gracias por tu comentario. Claro vere donde puedo subir el proyecto completo y lo agrego al post, esta hecho en NetBeans asi que no hay ningun problemas en importarlo y en la carpeta "dist" hayan el .war.

Imagen de pinha_xtreme

Duda transaction

Hola javAdicto, espero puedas profundizar un poco más en el tema de este patrón, ya que me surgieron algunas dudas, la primera de ellas: en que momento se invoca el método destroy() del administrador de sesiones, será cuando se pierda la referencia del bean que invoca el servicio de consulta???, la otra, veo que estas abriendo una transaccion, que pasa con la transaccion si nunca se le hace commit o rollback y si nunca hay operaciones de actualizacion o alta de datos, cual es el fin de abrir la transaccion?

Que paso pinha_xtreme

El metodo init() y destroy() se invocan automaticaente en un servlet, el primero para inicializar atributos o para solicitar recursos y el segundo para liberar estos recursos o lo que se haya solicitado, el metodo destroy() el cual cierra la session se manda a llamar cuando se termina la peticion(request). El otro punto se me paso realizar mas operaciones de ejemplo como el save(), load(), etc. Si es necesario el commit() para asefurar la transaccion o el rollback() en el catch si este genera una exepcion y dejar todo como estaba, despues expondre mas metodo de ejemplo. Gracias por comentar.

Sale, te expongo los metodos

interface GenericDAO:
 

clase metodos:
 

Imagen de jiturbide

Observaciones a la implementacion de open session in view

Hola

Es necesario hacer unas observaciones.
* Un filtro no es un servlet, es un filtro de servlets que intercepta peticiones, para que realmente se active debes colocar en el web xml que tipo de peticiones (jsps, servlets o un patron *) deben interceptarse por el filltro y hacer todo el trabajo dentro del metodo doFilter().
* Los metodos init y destroy de un filtro y un servlet solo se llaman en el arranque del server y en la finalizacion correcta de la aplicacion web, no por cada peticion.
* Lo que realmente esta pasando es que estas abriendo la sesion cuando se instancia el filtro en el arranque del server y esta sesion solo se va a cerrar cuando la aplicacion web finalice.
* Un filtro es una solucion muy costosa si filtras todas las peticiones, por ello se debe colocar en el web.xml solo las peticiones que deben ser filtradas y colocar dentro del metodo doFilter validaciones para que actue solo cuando sea necesario
* No conozco mucho de la combinacion Spring-Hibernate pero creo que debe haber alguna forma de hacer esto en la capa de integracion y no en la de presentacion.
* Suponiendo que el codigo es correcto, que pasaria con aquellas peticiones que no tengan que ver con alumno ni con profesor, de todas maneras abririan la sesion?
* Pecata minuta: es mejor Metodos que metodos

Envio un link de un libro para revisar el funcionamiento de los filtros

En este link toda la logica de filtrado esta dentro del metodo doFilter.

Saludos

Interesante

Es interesante lo que comentas. Por eso muy explicitamente puse: "si alguien encuentra algun error ya sea de logica, sintaxis, o de cualquier indole favor de avisarme para corregirlo". Gracias jIturbide por comentar, voy a checar lo que dices y cuando tenga el codigo corregido lo agregare en el post.

Imagen de jali

Muy bueno

Buen post! cuando me paso me estuve dando de topes jejeje.
Saludos

El cache de Hibernate

Otro punto a considerar en la implementacion de este Patron de Diseño es el echo de que Hibernate maneja dos niveles de Cache, por lo cual puede ser peligroso que nunca se haga un Flush de la sesion, ya que estará almacenando en el cache de segundo nivel todos los movimientos hasta que llegue un momento en que la aplicación se chute toda la memoria y se caiga, esto se puede corregir realizando un flush manualmente después de cada unidad de trabajo. Lo mejor seria integrar Spring con Hibernate para ahorrarte esos posibles dolores de cabeza.

Saludos

Que tal josebetomex

Buen punto el que expones, por que de eso se trata de evitar posibles problemas de rendimiento y sobre todo caidas del sistemas, pero sabes que seria excelente que acompañaras tu comentario con codigo de ejemplo, algo sencillo y practico, por que la mayoria de la gente que expone sus problemas o soluciones no lo hacen y eso seria como leer un libro de programacion que solo contiene teoria y definiciones, claro que las deficiones sirven pero de lo que realmente aprendes es de codigos de ejemplo que puedas compilar y ejecutar.

Cache de Hibernate

Que tal Javadicto

Un ejemplo sencillo podria ser el siguiente:

 

En ejemplo anterior se obtiene la sesion, se realizan determinado numero de operaciones, en este ejemplo 100000 inserciones y despues se cierra la sesion (Esto concluiria con un Flush). Hibernate en cada inserción estará almacenando en su cache de Segundo Nivel cada uno de los Pojos que estas creando, consumiéndose la memoria, lo cual provocara que en algún punto se la termine y truene la aplicación y nunca llegue a cerrar a sesion. Cabe aclarar que no es el Cache de primer Nivel que tipicamente se configura con EHCACHE.

Lo correcto cuando se usa Hibernate es cerrar la sesion despues de que has realizado cada bloque de trabajo, el ejemplo anterior se podria prevenir que se termine al memoria haciendo lo siguiente:

 

Otra forma de prevenir un truene es deshabilitando el cache de segundo nivel.

hibernate.cache.use_second_level_cache=false

Espero que con este ejemplo se aclare un poco la idea que quiero transmitir.

Saludos

Session Per Request (open session in view)

El metodo yo lo uso y es bueno, lo unico malo de usar un filtro es que wrapea TODOS los request, es decir, cuando el cliente pide una imagen, un css, un js... tambien se ejecuta. En mi caso uso tapestry5 y solamente uso el patron cuando se va a render() la pagina o un componente, haciendo que se llame solo cuando es necesario. Es un detalle de performance solamente. Saludos!

Imagen de ezamudio

T5

Si usas Tapestry 5, utiliza Assets para imágenes, CSS, javascripts, etc y configura el modo producción para que todos esos assets estáticos se copien a un directorio en el htdocs de tu web server, de modo que Tapestry (y el contenedor) ya no sirvan esos recursos sino que lo haga directamente el httpd.

Imagen de ale_imp

No entiendo

Hola buena tarde espero puedan ayudarme acabo de hacer un ejemplo, con la implementación que nos explico en este post javadicto. Mi inquietud es esta, tengo una aplicación en la cual estoy utilizando struts 2 y hibernate y me gustaria implementarle para fines de aprendizaje spring. Como lo mencione anteriormente mi aplicacion funciona prefectamente usanado estas tenologias antes mencionadas y con la Impleentacion de este post.. Por donde puedo empezar para q le agrege este patron open session in view pero usando spring. Aclaro es para fines de aprendizaje.

Imagen de ale_imp

Bueno acabo de encontrar un link

donde mencionan que se le agregue este filtro

 

Pero en la parte del applicationContex.xml como se definiria. Alguna idea

Imagen de benek

Antipattern

Tengan cuidado con Open Session In View, ha sido considerado un anti-patrón.

Les recomiendo esta lectura:

Saludos.