Archive for October, 2005

Una semana distinta

Ya tenía claro que esto de tener una empresa daba dolores de cabeza, que la gestión me consumiría mucho tiempo, y que al final, de eso de programar, más bien poco. Esta semana no es representativa, pero…
Y es que se puede decir (salvo por el teléfono e internet, que tenemos prestada gracias a Josetxu) que hoy hemos acabado de instalarnos en las nuevas oficinas. Vaya semanita que hemos tenido de paseos de una lado a otro, muebles montados y desmontados, idas y venidas, llamadas, listas de precios, etc.

Si tuviéramos que dar un premio a los más eficientes… a parte de a Gotxon y su furgoneta… supongo que se lo daría a Iberdrola, menos de 24 horas desde la solicitud de alta hasta tener la electricidad en el local. Lástima que aún no tengan PLC en Bilbao, estaría por contratárselo.


Menos mal que siempre queda Ibon para hacer el trabajo de verdad.

JSF: Excepciones más comunes y errores frecuentes

El problema de ser un blade runner de nuevos frameworks es que más de una vez nos encontramos con problemas que ni el google puede solucionar. En el caso de JSF es fácil encontrarse con infinidad de tutoriales (que más o menos enseñan lo mismo), un buen sitio de tutoriales sobre JSF sería este, por ejemplo.

El problema reside en encontrar sitios que comenten buenas prácticas a la hora de diseñar una aplicación JSF o que describa los errores más comunes a la hora de ponerse manos a la obra. En el caso de el archiconocido struts una de las páginas a las que más utilidad le hemos sacado a lo largo del tiempo es una simple página en la que se describen los errores más comunes y sus posibles soluciones, aquí.
Partiendo un poco de la idea de esa página pero aplicada a JSF, en Linking Paths hemos pensado en dedicar una página de este blog a describir las excepciones más comunes en JSF, sus posibles causas y un detalle de sus soluciones. Y por supuesto os animamos a todos a participar, para ir construyendo lo que puede ser una referencia para el sufrido programador que se adentra en territorios inexplorados ;-)

Podéis enviar vuestros errores (con solución, que no se trata de que esto se convierta en un foro de JSF) a través de los comentarios a este post, que prometo ir incluyéndolos en el cuerpo de la página. Y para predicar con el ejemplo:

Error: javax.faces.FacesException: Cannot get value for expression
Algún componente de JSF no puede acceder al value que hemos indicado de un backing bean
Causas Probables:

  • Comprobar que la clase y paquete del backing bean está bien escrita en faces.xml

  • Comprobar que existe la propiedad a la que hacemos referencia en el componente


  • Error: Al pinchar en un CommandButton, no hace nada y vuelve a la misma página

    Causas Probables:

  • Comprobar que la clase y paquete del backing bean está bien escrita en faces.xml

  • Si en el form no existe ningún campo que esté relacionado con una propiedad (no con un método) del backing bean, NO se creará al entrar en el form, por tanto no se podrá llamar a sus métodos (que siempre devolverán null y por tanto te mostrarán la página de nuevo, ya que null a la vuelta de una llamada hace que se vuelva a mostrar la página que la originó). Se debe usar un CommandLink en esos casos; si queremos que tenga apariencia de botón hacerlo a través de style


  • TomaHawk Error: javax.faces.FacesException: java.io.NotSerializableException:
    - Parece que suele suceder cuando un commandbutton vuelve a la misma pagina que lo originó y se debe actualizar otro componente.
    Causas Probables:

  • La clase a poner en la tabla en sus columnas DEBE ser serializable, usar una clase auxiliar que lo sea si la original no puede serlo.

  • El método para guardar el estado debe ser CLIENTE. En web.xml:

      
        javax.faces.STATE_SAVING_METHOD
        client
      
    




    Pues nada, a ver si os animáis y nos mandáis vuestros errores ;-)

    ACTUALIZACIÓN:
    Error: java.lang.IllegalStateException: No Factories configured for this Application - typically this is because a context listener is not setup in your web.xml.
    A typical config looks like this;

    org.apache.myfaces.webapp.StartupServletContextListener



    Causas Probables

  • Comprobar la sintaxis xml de faces-config.xml
  • ¿Dónde esta Linking Paths S.L.?

    Si hace unos días os informábamos de que oficialmente teníamos oficina, hoy os podemos decir que ya se puede (casi) trabajar en ella, de modo que os dejamos un plano de su localización.

    Francisco Macia 11, 7º
    48014 Bilbao

    Los amigos, los conocidos y los potenciales clientes están invitados a tomar un café de inauguración.

    DENIM: deja de usar el boli!

    Uno de los problemas a los que siempre me enfrento a la hora de diseñar una aplicación es la interfaz de usuario. Cada vez creo más cierto que hay que empezar a pensar en la arquitectura de la aplicación a partir de lo que los usuarios de ella quieren/necesitan. Y no soy el único sino mira.

    Pero este post no va de eso. En todos los estados previos de definición de la aplicación siempre nos encontramos rodeados de folios sacados de la impresora donde hacemos bocetos de lo que será la aplicación de usuario. Incluso para tener las ideas más claras se opta a realizar una página web en HTML simple sólo para ver cómo se relacionarán los distintos elementos, y si tienes un diseñador gráfico a mano ;-) que te la haga en photoshop/gimp. Pero siempre me he encontrado con el problemita de no encontrar gráficos hechos para hacer algo rápido (¿cómo hacéis si queréis meter un calendario o un reloj, por ejemplo?)


    Pues bien, hoy buceando en los entresijos de la web he encontrado una pequeña joyita: denim
    Está realizado en java (cómo no! ;-P) y permite realizar bocetos en pantalla de lo que queremos/creemos va a ser la GUI de la aplicación. Como muestra un bocetillo chorra:

    Un pantallazo de denim


    La interfaz me resulta hasta divertida (en vez de pantallas distintas lo que tienes es zooms de tu boceto), y viene con algunos componentes ya hechos que sólo tienes que arrastrar a tu boceto.
    Me ha gustado la idea y la propia UI de denim me ha dado algunas ideas interesantes. No sé tal vez debería mirarme mi nivel de frikismo ;-)

    Cuatro paredes y un techo

    Tenemos oficina. Este sábado toca mudanza. Pues sí, ya tenemos oficina en Bilbao, y dejamos de trabajar de forma distribuida. Con esto esperamos que podamos acelerar nuestra integración dentro de la cultura de la empresa. Para el que conozca… en la calle Francisco Macía 11, al lado de la Universidad de Deusto.

    La verdad es que en el pasado pensábamos que el trabajo distribuido iba a ser más fácil de lo que ha resultado en realidad. Quizás el hecho de distribuirnos en nuestro hogar personal (es más duro de lo que podríais pensar) haya sido el problema, pero aunque el Skype lo ha facilitado, para nosotros no ha sido la experiencia que esperábamos.

    ¿Como debería ser un CMS sencillo?

    Por distintas razones, estamos desarrollando un CMS basado en el servicio de repositorio (JCR, JSR 170) de OCAS y todos los estándares, así como una interface con JSF + AJAX. El objetivo principal es la de hacerlo muy muy muy sencillo de utilizar. El resultado lo tenemos bastante claro (si alguien quiere ver el PDF del producto sólo tiene que pedirlo), aunque la parte del backoffice nos esta planteando varias dudas, y es que cometimos el error de sondear a varios usuarios sobre como debería ser para que les resultara sencilla su utilización, la mayor queja que presentan todos los CMS.


    Al final nos hablaron siempre de dos familias de soluciones: editar directamente desde el front y un administrador sencillo con las opciones mínimas. En el primer caso, con un botón de editar estarían contentos, sin añadir todas las opciones, y en el segundo consideran que una analogía del árbol de carpetas sería bastante acertada, pues todo el mundo la entiende. Ninguna gran sorpresa.

    Me alegra saber que andábamos bien encaminados. Aunque lo que puede tener sentido para un producto sencillo (en realidad nuestro CMS tendrá cinco sabores) puede ser un problema para el resto, pero lo iremos solventando sobre la marcha, supongo.

    ¿Tiene esta aplicación sentido empresarial?. Bueno, como producto separado posiblemente no mucho (does the world need a new CMS?), pero encaja dentro de otra serie de ideas y productos, combinando componentes, así que la inversión nos parece necesaria. Aunque la licencia será con prácticamente toda la seguridad una GPL (lo que no quiere decir que sea gratis!), de modo que quizás cobre algo más de sentido.

    La solución a los problemas con jbpm: ¿Un nuevo patrón para usar hibernate?

    Pues nada, que creo que ya hemos solucionado el tema, o por lo menos eso dicen mis tests ;-). Y la solución que hemos tomado no la veo por ningún lado de hibernate.org, y en algunos casos puede ser muy útil: cuando hacemos uso de una librería que haga uso de hibernate y no queremos obligar al programador a conocer los entresijos de las sesiones y las transacciones, pero a la vez queramos hacer uso de la caché de primer nivel de forma eficiente. Perdonad lo pretencioso del título pero es que me ilusiono rápido, a ver si me hacéis caer de las nubes y me decís los problemas que le véis :-) (cuatro ojos ven más que dos)

    Veamos, el problema con jbpm es que usaba el lazy loading ampliamente a lo largo de su código. Al dar implementación al workflow de OCAS nos encontramos con el siguiente problema: se creaba un proceso en una sesión de jbpm (=sesión hibernate), pero además teníamos un método process.getVariables() en el que teníamos que abrir una nueva sesión jbpm, cargarnos de la base de datos el proceso (que ya teníamos en memoria), y entonces hacer el jbpmProcess.getVariables() (y el lazy loading funcionaba porque ahora el proceso SÍ estaba cargado en la misma sesión en la que iba a cargar las variables). Un poco de código:

    
    public class JBPMWorkflowService implements WorkflowService{
    (...)
    public net.javahispano.ocas.workflow.Process startProcess(String processName, String usr) {
            JbpmSession jbpmSession = jbpmSessionFactory.openJbpmSession();
            org.jbpm.graph.def.ProcessDefinition definition = jbpmSession
                    .getGraphSession()
                    .findLatestProcessDefinition(processName);
            ProcessInstance jbpmProcess = new ProcessInstance(definition);
            jbpmSession.getGraphSession().saveProcessInstance(jbpmProcess);
            jbpmSession.close();
            JBPMProcess proceso = new JBPMProcess(jbpmProcess);
            return proceso;
        }
    }
    
    public class JBPMProcess implements Process{
    (...)
    public Map getVariables(){
            JbpmSession sesion = wfService.getJbpmSession();
            ProcessInstance process = sesion.getGraphSession()
                    .loadProcessInstance(processInstance.getId());
            Map result = process.getContextInstance().getVariables();
            wfService.closeJbpmSession(sesion);
            return result;
    }
    


    Buff HORRIBLE! Y ¿porqué lo teníamos que realizar así? Porque realmente estabamos usando el patrón de hibernate session-per-request lo que pasa es que OCAS es una librería cuyas peticiones van a venir de otro código, que no sabemos exactamente a qué métodos va a llamar (en algún sitio puede que sólo llamen a startProcess, en otros puede llamar a los dos métodos presentados).

    En una aplicación web, el patrón session-per-request tiene sentido ya que conocemos que vamos a hacer en cada petición (traernos datos de la bd, modificarlos y salvarlos), pero nuestro problema es que no conocemos a priori que métodos iban a ser llamados, con lo cual debíamos contemplar cada llamada al método del wfService como una petición independiente.

    De repente surgió la idea: el patrón sesión por transacción de aplicación, pero un poco al revés. En hibernate dicen que usando los métodos connect y disconnect puedes hacer que una sesión dure varias transacciones; pues bien lo que necesitábamos es usar los métodos connect y disconnect entre las llamadas a los métodos del WF, con ello conseguiríamos:

  • No abrir una nueva sesión en cada llamada a los métodos

  • Aprovecharnos de la caché de primer nivel de hibernate: por ejemplo si alguien llamase al método getVariables dos veces seguidas, solamente en la primera habría un acceso a la base de datos


  • Claro está, que no hay nada gratis, para poder implementar la idea nos obligaba a idear algún sitio donde la sesión se abriese por primera vez y se cerrase de una vez por todas, así que “algo” sí hemos tenido que cambiar la interfaz: ahora aparece el concepto de ProcessTransaction (con sus métodos open(), commit() y rollback de toda la vida ;-)) Pero eso sí, para nada aparece el concepto de ProcessSession, no existe, las sesiones jbpm son transparentes al programador que use OCAS


    Otro poco de código:

    public class JBPMProcessTransaction implements ProcessTransaction{
        private JbpmSessionFactory sesionFactory;
        private JbpmSession jbpmSession;
        private Session hibernateSession;
    
       (..)
    
        public void open() {
    
            if(jbpmSession.getCurrentJbpmSession() == null){
                jbpmSession = sesionFactory.openJbpmSession();
            }else{
                jbpmSession = jbpmSession.getCurrentJbpmSession();
            }
            jbpmSession.beginTransaction();
            jbpmSession.getSession().disconnect();
    
        }
    }
    


    Y ahora el método de antes cambia así:

    public Map getVariables(){
            JbpmSession sesion = JbpmSession.getCurrentJbpmSession();
            sesion.getSession().reconnect();
    
            Map result = processInstance.getContextInstance().getVariables();
            sesion.getSession().disconnect();
            return result;
    }
    


    Para que esto funcione, el usuario debe abrir y cerrar transacciones con el servicio de workflow:

    ProcessTransaction transaction = new JBPMProcessTransaction();
    transaction.open();
    wfservice.deployProcessDefinition(processDefinition);
    net.javahispano.ocas.workflow.Process proceso = wfservice.startProcess("TestModel", "Test");
    wfservice.stateEnded(proceso);
    
    assertEquals(proceso.getVariable(JBPMProcessDefinition.AUTHOR), "Test");
    assertEquals(proceso.getVariable(JBPMProcessDefinition.DESCRIPTION), "Test description");
    transaction.commitAndClose();
    


    El método getCurrentSession(), es un método que proporciona jbpm y que funciona como un HibernateUtil (ver primer post de la serie).

    En todas las llamadas a esos métodos se usa la misma sesión jbpm (que al desconectarse entre llamadas a métodos no ocupa una conexión del pool), aprovechándose de su caché, y cuando finalmente se llama a transaction.commitAndClose(), todo se guarda en la BD y se cierra la conexión.

    En cuanto a hibernate, el pretencioso título de este post quiere decir, que he visto como patrón una sesión larga con varias transacciones asociadas, pero que lo que se trata es de una transacción con una sesión que se conecta y desconecta. No hemos inventado nada pero para este tipo de problemas, como el que se ha planteado, puede ser la solución genérica.
    Ahora mismo le estamos dando vueltas pero “intuimos” que si guardasemos la ProcessTransaction en un objeto HttpSession, se podría implementar con el mismo código el patrón “Long Session” de hibernate (lo de que una sesión permanece a través de varias transacciones, que en nuestro caso lo que queremos es que la sesión permanezca a través de varias peticiones, que pueden ser de una aplicación web o de escritorio).

    Ya os contaré (si aguantáis mis tostones ;-))

    PD: ya sé que sigue siendo feo, tener esas llamadas a reconnect y disconnect en cada llamada a los métodos del workflow de OCAS, la idea es que en la próxima refactorización se usará AOP, pero ya llegará ese post.

    Sesiones y transacciones en hibernate: el “problema” de tirar pa’arriba

    Sigo con mi serie de posts sobre el desarrollo de OCAS. En este intentaré describir mi problema, y en el próximo lo que creo que va a ser la resolución de esta apasionante serie :-P.

    El objetivo que nos hemos marcado en OCAS es proporcionar una capa de servicios que ayuden a la programación en tareas típicas a los que nos enfrentamos proyecto a proyecto, abstrayendo la complejidad de las librerías a los programadores. De la misma forma nuestra idea es dar diferentes implementaciones a esos servicios para que el desarrollador elija la que más le guste. En breve el wiki contendrá más información.
    En estos momentos me encuentro desarrollando la implementación del servicio de workflows con la librería open source JBPM, que a su vez usa como mecanismo de persistencia hibernate. Y es aqui donde empezó la paja mental causante de esta serie de posts ;-)

    Veamos, jbpm ofrece en su API unas “sesiones” jbpm, que no son más que un wrapper de las sesiones hibernate. A partir de una sesión jbpm se obtienen unos objetos sesión (GraphSession, TaskMgmtSession…), que yo calificaría como una especie de DAO’s (para entendernos ;-)) para trabajar (CRUD) con determinados aspectos de un proceso. El tema es que en jbpm hacen aaaampliooooo uso del lazy loading (las variables de un proceso son lazy por ejemplo). Hasta aquí puedo leer ;-), que me reservo un post sobre jbpm cuando lo tenga acabado.

    Y aquí es donde empezamos a chocar con OCAS: nuestra pretensión es facilitar la vida al programador, no decirle que tiene que aprender hibernate para usar un servicio de workflows, y que tiene que tener claro que es una sesión y que es una transacción en hibernate. Y aquí es donde me parece que pecan algunas librerías que usan hibernate, simplemente transportan la interfaz de hibernate hacia arriba y le cuentan al programador que se mire la documentación. Por ejemplo, en jbpm hace falta conocer hibernate y su manejo de transacción/sesión y los patrones involucrados (ver post anterior) para entender porqué narices sale esa LazyInitializationException cuando pido las variables de un proceso fuera de la sesión jbpm que lo cargó. Para mí no tiene mucho sentido. Generalmente el código que usa hibernate adquiere una fuerte dependencia con él, lo cual tiene mucho sentido en DAO’s de acceso a BD, pero que deja de tenerlo cuando simplemente son librerías que hacen otra cosa además de usar hibernate ;-)


    En mi implementación del servicio de OCAS he barajado varias soluciones para no molestar al usuario programador con los detalles de la implementación concreta en jbpm:

  • Modificar el código de jbpm para que determinadas relaciones no sean lazy: no sigue la filosofía de OCAS, en la que damos la implementación de nuestros servicios sin tocar el código fuente de las librerías.

  • Abrir y cerrar sesión en cada uno de los métodos de mi servicio de workflow. Por ejemplo así:

    public Map getVariables(){
            JbpmSession sesion = wfService.getJbpmSession();
            ProcessInstance process = sesion.getGraphSession().loadProcessInstance(processInstance.getId());
            Map result = process.getContextInstance().getVariables();
            wfService.closeJbpmSession(sesion);
            return result;
    }
    


    Buff, para mí apesta :-), se podría ver como una sesión por petición, pero el problema es que aquí no se trata la petición de un usuario web, sino de una llamada a un método de una librería, y en un request de una aplicación web puede haber varias decenas de llamadas a métodos. No se aprovecha el caché de primer nivel y además me obliga a cargar de nuevo el proceso desde la BD (porque antes lo cargué en una sesión jbpm/hibernate distinta) aunque ya lo tenga en memoria. Como ventaja lo único que puedo decir es que en la interfaz ofrecida del servicio al programador el no ve ni sesiones ni transacciones de hibernate, lo cual es mi objetivo.


  • Alguien podría decirme:

    Bueno pues cargate toda la info del proceso en memoria cuando se cargue el proceso y montate tu propia caché


    Puede ser, pero creo que he encontrado una solución mejor –> más productiva y eficiente (y sin escribir mucho más código)



  • Pero la solución (que desvelaré en un próximo post para mantener la intriga, y poder hacer los tests :-)) me vino el día de la hispanidad (como buen programador yo no tengo fiestas :-() mientras fregaba mi taza del café. Sólo os adelanto que tiene que ver con uno de los patrones que describen en hibernate, y que los usuarios de OCASWF sólo deben conocer el significado de una transacción (el general, el que conoce todo el mundo vaya). Os dejo con este atroz suspense, para que estos posts sirvan para ver las ventajas de usar OCAS y los sudores que nos cuestan.

    Salu2
    PD: Sólo espero que hibernate 3 haga todo lo que promete :-D

    ¿Está Java Server Faces preparado para la vida real?, ¿ya?

    En los últimos tiempos, estamos en Linking Paths trabajando con Java Server Faces en algunos de nuestros productos (más información en breve). No porque creamos que es el framework definitivo y mejor que todos los demás, sino más bien como una apuesta estratégica de cara al futuro. Pero después de 5+ años haciendo aplicaciones web, digamos que nos plantea tanto dudas como respuestas.
    Java Server Faces nos ofrece una serie de ventajas indudables (componentes configurables, la promesa de crear distintos clientes cambiando el renderer, ocultar en parte que es una aplicación web, promover el trabajo con backing beans e IoC, ser un estándar, etc. ), pero a la vez nos de muestra que es un entorno al que aún le faltan un par de vueltas. En mi opinión dos principalmente, aunque algunas decisiones sobre la arquitectura podrían ser discutibles, algo que no voy a hacer porque quiero pensar que las personas que tomaron esas decisiones saben más que yo.


    Volviendo a los dos grandes problemas actuales:


    • Forma estándar de distribuir y configurar los nuevos componentes.

    • Información.



    En el primer caso, nos encontramos proyectos como Tomahawk (que pertenece a myfaces) que nos ofrece componentes varios para nuestras aplicaciones (menus paginadores, pestañas, etc.), pero no hay forma sencilla de utilizarlos. Y con sencilla quiero decir: añadirlos al IDE de nuestra elección para poder usarlos con pinchar y arrastar, que es otra de las bondades que se esperan de JSF.


    Respecto a la información, me refiero a la situación inherente de toda nueva tecnología. Java Server Faces es nuevo, y como tal la cantidad de información es menor que la disponible para, digamos, Struts. Y no me refiero a libros, sino información de proyectos reales, de los problemas que se han ido encontrando, etc. No puedo evitar tener la sensación de estar en el año 1999 (obviamente mejor situación, en entorno mucho más acotado y mucha más experiencia), en la que todo lo que había era el API de Servlets, y en las que cada uno iba tirando por dónde mejor le parecía (de ahí la variedad de frameworks web en Java).

    De modo que, volviendo a la pregunta inicial… ¿está Java Server Faces preparado para un uso real en la empresa?. Obviamente nuestra respuesta es sí. A pesar de esos pequeños drawbacks (y sin entrar en peleas con otros, como Spring Framework o Ruby on Rails), Java Server Faces es una posibilidad más que ha venido para quedarse y con buenas perspectivas de futuro, y nadie que se dedique al desarrollo web en Java debería perderlo de vista (que lo use o no es decisión de cada uno). Sin duda el primero de esos grandes problemas que he comentado lo acabarán solventando, y el segundo… digamos que el segundo se solventa día a día.


    Lo que ya ofrece, lo que es seguro que ofrecerá, así como principalmente el hecho de ser un estándar mimado, hacen de Java Server Faces una buena apuesta de futuro con un uso factible hoy.

    Por poner nuestro granito de arena, hemos creado una lista de distribución para todos aquellos interesados en Java Server Faces: jsf@dev.linkingpaths.com., así como lo seguiremos impartiendo en nuestros cursos.

    Sesiones en Hibernate Vs Sesiones en Spring

    Embarcado en el desarrollo del servicio de workflow de OCAS y en su implementación con JBPM me he tropezado (de nuevo) con un iceberg: las sesiones de Hibernate y el cómodo/incómodo lazy loading. Existe en javahispano un estupendo artículo de Martín sobre las sesiones y las transacciones de Hibernate que recomiendo a todo el mundo; pero intentaré resumir aquí cuál es el problema (a ver si lo consigo).

    El lazy loading o carga perezosa es un mecanismo que hibernate implementa para tratar algunas relaciones. Una entidad (POJO que se persiste en una tabla de la BD) puede tener múltiples relaciones con otras entidades (por ejemplo, un producto puede tener una Collection de los comerciales que lo venden). A veces cuando nos traemos datos de la BD no nos interesa cargar toda la información relacionada ya que sólo vamos a usar o mostrar una parte. Así que, siguiendo con el ejemplo, si queremos mostrar el número de referencia de un producto y su nombre en una interfaz de usuario, cargar los datos de los veinte comerciales que lo venden en memoria ocupa espacio innecesariamente.
    Usando el lazy loading, si definimos una relación como lazy (poniendo el atributo de la relación lazy a true en los mappings de hibernate), Sólo se cargará la información de los vendedores CUANDO accedamos a la Collection vendedores. Así por ejemplo:

    producto.getVendedores(2).getName();



    lo que provocará es un nuevo acceso a la base de datos para cargar la información de los vendedores. Llamarlo carga perezosa obedece a que no hemos tenido que escribir las líneas de código para traerlo de la base de datos: hasta que no hagamos uso de esa relación no se cargará en memoria, y cuando hagamos uso de ella “automágicamente” se traerá la información desde la base de datos. Por tanto para el programador resulta un mecanismo muy cómodo que permite realizar aplicaciones que aprovechan muy bien la memoria, sin cargar datos innecesarios hasta el momento que son necesarios.


    EL PRECIO DEL LAZY LOADING
    Claro está que cuando he escrito automágicamente nadie habrá pensado en Gandalf trayendo nuestros datos. Lo que hace hibernate es crear una clase wrapper de nuestra Collection y asociarla al objeto (por ejemplo un Set de vendedores). Estas clases wrapper actúan como un proxy: son clases herederas de las correspondientes Collection cuyos métodos están sobreescritos por los chicos de hibernate y lo que hacen es traer de la base de datos la entidad correspondiente. Pero para traer algo de una base de datos en Hibernate ES NECESARIO que la misma sesión de Hibernate en la que te has cargado el objeto original esté abierta. Y no se te ocurra pedir a los de hibernate que se pueda hacer abriendo una nueva sesión (sino quieres que se mofen de ti) porque como explican aquí se cargarían el concepto de transacción.

    Hibernate sólo puede asegurar que el objeto original existe dentro de la misma transacción/sesión, por lo que si abres una nueva sesión deberás volver a cargar el objeto original y luego su Collection de entidades relacionadas.
    Y aquí es cuando empiezan los problemas ;-)


    ERRORES DE NOVATO EN HIBERNATE
    Hace un año, para un proyecto que me tocó decidí utilizar hibernate para matar dos pájaros de un tiro (el proyecto era pequeño y así yo aprendía a usar hibernate). Tranquilos, pagué mi audacia con muchas horas de lectura de manuales y artículos :-D


    Todo ufano cree un montón de relaciones lazy porque así mi aplicación web iba a ser la leche en verso (sólo en memoria lo que necesitara). Cuando llegué a mi primer jsp en el que además de datos de un producto de un almacén debía mostrar el nombre de su encargado (relación lazy por supuesto, ya que el encargado igual llevaba 700 piezas) me saltó la bonita LazyLoadingException. Vale, oh que tonto soy, el nombre del encargado viene a través de una carga perezosa y yo he cerrado la sesión de Hibernate al traer el producto. Pero…. glups!, si el único sitio en el que hago producto.getEncargado().getNombre() es en la página JSP!! Y yo abro la sesión en mi DAO y allí debería cerrarla (como chico aplicado y cumplidor que soy ;-)). ¿Que hago? ¿Le paso a la página jsp la sesión para que la cierre en cuanto me traiga el nombre del encargado? ¿Me tiro en postura fetal y espero que Dios arregle mi código? :-D


    Tras secar el sudor frío de mi frente, me agencié “Hibernate in Action” el libro escrito por los desarrolladores de Hibernate y me puse a indagar cómo solucionaban ellos este problema.

    Patrones, anti-patrones y opiniones
    Vale, pues en Hibernate 2.1 (luego hablamos de la 3 que esto va para largo) explican claramente los distintos patrones que hay (y que Martín explica excelentemente en su artículo, leedlo!), recomendando el patrón session-per-request: por cada petición del cliente se abre una sesión, se trae de la BD lo que se tenga que traer y se cierra la sesión.


    El tema está en ¿cómo lo implementamos en una aplicación web? Bueno pues en Hibernate 2.1 proponen una clase (HibernateUtil) que usando variables ThreadLocal (explicación de las poco conocidas variables ThreadLocal ) consiguen la magia, haciendo que cada petición (que en una aplicación web genera un thread) tenga a su disposición la creación de sesiones. Un pequeño resumen de la clase a continuación:

    public class HibernateUtil {
        private static Configuration configuration;
        private static SessionFactory sessionFactory;
        private static final ThreadLocal threadSession = new ThreadLocal();
        private static final ThreadLocal threadTransaction = new ThreadLocal();
        private static final ThreadLocal threadInterceptor = new ThreadLocal();
        (...)
    
        /**
         * Retrieves the current Session local to the thread.
         *
         * If no Session is open, opens a new Session for the running thread.
         *
         * @return Session
         */
       public static Session getSession() throws InfrastructureException {
            Session s = (Session) threadSession.get();
            try {
                if (s == null) {
                    if (getInterceptor() != null) {
                        s = getSessionFactory().openSession(getInterceptor());
                    } else {
                        s = getSessionFactory().openSession();
    
                    }
                    threadSession.set(s);
                }
            } catch (HibernateException ex) {
                throw new InfrastructureException(ex);
            }
            return s;
        }
    
        /**
         * Closes the Session local to the thread.
         */
        public static void closeSession()throws InfrastructureException {
            try {
                Session s = (Session) threadSession.get();
                threadSession.set(null);
                if (s != null && s.isOpen()) {
                    s.close();
                }
            } catch (HibernateException ex) {
                throw new InfrastructureException(ex);
            }
        }
    
    (...)
    /**
         * Register a Hibernate interceptor with the current thread.
         *
         * Every Session opened is opened with this interceptor after
         * registration. Has no effect if the current Session of the
         * thread is already open, effective on next close()/getSession().
         */
        public static void registerInterceptor(Interceptor interceptor) {
    
            threadInterceptor.set(interceptor);
        }
    
        private static Interceptor getInterceptor() {
            Interceptor interceptor = (Interceptor) threadInterceptor.get();
            return interceptor;
        }
    


    No he posteado la creación de la SessionFactory (se crea en un bloque de código estático), pero creo que el código se explica sólo ;-) Con este código es posible definir un Interceptor en Hibernate, que será llamado al abrir o cerrar una sesión, para hacer lo que queramos en esos eventos.


    Y un pequeño ejemplo de código donde se usa (por ejemplo dentro de un DAO):

    HibernateUtil.beginTransaction()
    Query query = HibernateUtil.getSession().getNamedQuery("query1");
    result = query.setEntity("usuario", usuario).list();
    return result;
    


    Uopss!! ¿Y donde has dejado el closeTransaction y el closeSession? Pues como decía antes, NO puedo cerrar la sesión en el DAO porque eso me quitaría la posibilidad de acceder a las relaciones lazy desde mi página jsp. Según el patrón sesion-per-request, la sesión se cerrará cuando toda la información se haya enviado y dibujado en la interfaz del usuario. Y en una aplicación web el sitio para hacerlo es un filtro de Servlet, que se ejecuta cuando la página jsp ya ha terminado de dibujar la pantalla. Como muestra un botón:

    public class HibernateFilter implements Filter {
    
    
        private static Log log = LogFactory.getLog(HibernateFilter.class);
    
        public void init(FilterConfig filterConfig) throws ServletException {
            log.info("Servlet filter init, now opening/closing a Session for each request.");
        }
    
    
        public void doFilter(ServletRequest request,
                             ServletResponse response,
                             FilterChain chain)
                throws IOException, ServletException {
    
    
            // There is actually no explicit "opening" of a Session, the
            // first call to HibernateUtil.beginTransaction() in control
            // logic (e.g. use case controller/event handler) will get
    
            // a fresh Session.
            try {
                chain.doFilter(request, response);
    
                // Commit any pending database transaction.
    
                HibernateUtil.commitTransaction();
    
            } finally {
    
                // No matter what happens, close the Session.
                HibernateUtil.closeSession();
    
    
            }
        }
    


    De esta forma cuando se haya dibujado la página jsp, se realizará un commit de todos los cambios a la base de datos y se cerraran la sesión que haya abierto el cliente (una sesión por petición).
    Existe otra variante que en lugar de cerrar sesiones, las conecta y las desconecta (de la conexión JDBC subyacente).

    Esta forma de trabajo, en la que abrimos sesiones en un lado y las cerramos en otro no acabó de convencer a todo el mundo, ni a mí, ya que hace a tu aplicación dependiente de Hibernate, no sólo en los DAO’s sino incluso en la capa de la aplicación web. Con Hibernate 3 el modelo cambió.

    HIBERNATE 3: Donde dije Diego..

    Como aparece aquí , todo ha cambiado en Hibernate 3, en el que han ampliado la clase SessionFactory con el método getCurrentSession(). Así que ahora podemos tener:

    try {
        factory.getCurrentSession().beginTransaction();
    
        // Do some work
        factory.getCurrentSession().load(...);
    
        factory.getCurrentSession().persist(...);
    
        factory.getCurrentSession().getTransaction().commit();
    }
    catch (RuntimeException e) {
        factory.getCurrentSession().getTransaction().rollback();
    
        throw e; // or display error message
    }



    No he mirado el código (el tiempo, ese escaso bien) pero “huele” bastante a que la implementación de ese método usará ThreadLocal internamente como el HibernateUtil.

    Para librarnos de abrir y cerrar transacciones, de nuevo el filtro de servlet. Al ejemplo puesto antes ahora lo llaman patrón Open Session in View (¿verdad que es chulo llamar patrón de diseño a toda solución? ;-)) y está descrito aquí . La discusión de porque no crea una “dependencia real” (sic) con Hibernate aquilicuá . Que cada uno saque sus conclusiones, pero ya os aviso del tono de los Hibernate Boys: “si no estás de acuerdo conmigo es porque no tienes ni pajolera idea”. Aunque a veces resulta divertido escuchar a un prepotente, a la larga cansa :-D


    En Hibernate 3, y como cada vez se acercan más al estándar EJB 3.0 (¿porqué será? ;-)), también existe la posibilidad de marcar las transacciones no sólo con el filtro de servlet, sino también con JTA. Así definiremos en un descriptor de despliegue (EJB2) o con annotations (EJB3) que determinados métodos que llamamos, requieren el inicio de una transacción, así nuestro código quedará más “bonito”:

     // Do some work
    factory.getCurrentSession().load(...);
    factory.getCurrentSession().persist(...);
    


    Y tendremos una sesión por transacción (tal y como recomiendan en el patrón sesion-per-request).

    Recapitulando:
    - O usamos JTA, en un entorno EJB
    - O usamos el open session in View, con un filtro de servlet que nos abra transacciones.

    Y en eso que llega la “primavera” y a todos la sangre altera ;-)

    SPRING/HIBERNATE
    Los desarrolladores de Spring en su afan de hacer la vida más fácil a los programadores proporcionaban desde Hibernate 2 unas clases de apoyo para integrar Hibernate en una filosofía IoC. EL SessionFactory de Hibernate se define como un bean en el contexto de la aplicación. Usando XML el bean se define así:

    
      
      
        
    
        
        
    
      
      
        
    
        
          
            product.hbm.xml
          
    
        
        
          
            net.sf.hibernate.dialect.MySQLDialect
    
          
        
      


    Y para que nuestros DAO’s tengan acceso a ese SessionFactory:

    
    
        
      
    
    

    Así, el código quedaría:

    public class ProductDaoImpl implements ProductDao {
        private SessionFactory sessionFactory;
        public void setSessionFactory(SessionFactory sessionFactory) {
            this.sessionFactory = sessionFactory;
        }
    


    Primera gran diferencia: NO necesitamos HibernateUtil. Es más en el manual de referencia lo indican:

    We strongly recommend such an instance-based setup over the old-school static HibernateUtil class from Hibernate’s CaveatEmptor sample application! (In general, do not keep any resources in static variables unless absolutely necessary!)


    Toma torpedo a la línea de flotación de Gavin King. Y claro luego veremos como se toma las cosas el Gavin ;-D
    Para acceder a las sesiones (y por tanto empezar a usar las posibilidades de hibernate) ofrecen varias alternativas:
    - Usar la clase HibernateTemplate, que además de tener implementados ciertos métodos útiles (find, load, saveOrUpdate, o delete) permite “callback implementation” (una forma de realizar los punteros a funciones en Java, info ):

    public Collection loadProductsByCategory(final String category) throws DataAccessException {
        HibernateTemplate ht = new HibernateTemplate(this.sessionFactory);
        return (Collection) ht.execute(new HibernateCallback() {
            public Object doInHibernate(Session session) throws HibernateException {
                Query query = session.createQuery("from test.Product product where product.category=?");
                query.setString(0, category);
                return query.list();
            }
        });
    }
    


    - Usar como base de tus DAO’s la clase HibernateDAOSupport, y dentro de esta usar el HibernateTemplate, o la API de Hibernate directamente:

    public class ProductDaoImpl extends HibernateDaoSupport implements ProductDao {
        public Collection loadProductsByCategory(String category) throws DataAccessException {
            return getHibernateTemplate().find(
                "from test.Product product where product.category=?", category);
    
        }
    }
    
    public class ProductDaoImpl extends HibernateDaoSupport implements ProductDao {
        public Collection loadProductsByCategory(String category)
                throws DataAccessException, MyException {
    
            Session session = getSession(getSessionFactory(), false);
            try {
                List result = session.find(
                    "from test.Product product where product.category=?",
    
                    category, Hibernate.STRING);
                if (result == null) {
    
                    throw new MyException("invalid search result");
                }
    
                return result;
            }
            catch (HibernateException ex) {
                throw convertHibernateAccessException(ex);
            }
    
        }
    }
    


    - Usar la API de HIbernate 3 para obtener las sesiones:

    public class ProductDaoImpl implements ProductDao {
    
        private SessionFactory sessionFactory;
        public void setSessionFactory(SessionFactory sessionFactory) {
            this.sessionFactory = sessionFactory;
        }
        public Collection loadProductsByCategory(String category) {
    
            return this.sessionFactory.getCurrentSession()
                    .createQuery("from test.Product product where product.category=?")
                .setParameter(0, category)
                .list();
    
      }
    }
    


    En los dos primeros casos la ventaja de usarlos en Hibernate 2 es clara: no nos tenemos que ocupar de abrir o cerrar sesiones, lo hace el HibernateTemplate por nosotros, además de hacer que nuestras sesiones entren a formar parte de una transacción (de Spring, de la que luego hablaré).
    Claro está que para solucionar el tema del LazyLoading con el que empezé este ladrillo la solución es semejante a la de Hibernate, usar un filtro o un interceptor . Lo bueno es que es el HibernateTemplate el que comprueba si tenemos una sesión asociada al thread o no y cierra la sesión si no existe este filtro (Alguno podría decir que eso también se puede implementar en un HibernateUtil: pues sí, pero con Spring “viene de fábrica” ;-)

    En la tercera alternativa el código es el mismo que el que usaríamos en Hibernate (salvo que obtenemos el SessionFactory por IoC), con la salvedad de que ahora podemos usarlo sin necesidad de definir transacciones con JTA, sino con los mecanismos de Spring. Y aquí es donde empiezan las leches entre las divas del bel canto ;-D

    DEPRECA LO QUE ME APETECE, INSUSTANCIAL!
    Más o menos es lo que parece querer Gavin King respecto a Spring. A raíz de que de la página de hibernate “desaparecieran” las referencias a Spring la polémica estalló, y cómo no, en un blog


    Más o menos, Gavin King viene a decir que HibernateTemplate es una bazofia poco elegante y que los admiradores de Hibernate no deberían usarla ya que en Hibernate 3 está tirado obtener sesiones y el manejo de transacciones (con JTA). Y que la “deprequéis” chavalillos de Spring. A lo que los de Spring responden: es mi código y hago lo que quierto con él, sino quieres usar HibernateTemplate NO LO USES, pero no molestes :-D


    Vale, pero es una bazofia tan poco elegante como HibernateUtil propuesto en Hibernate 2 (yo diría que menos). Vamos que ha sido una bazofia que ha habido que usar bastante con Hibernate 2.

    Por otro lado, se le “olvida” comentar un pequeñísimo detalle, que es la madre del cordero del asunto: con Hibernate si quieres usar getCurrentSession() TIENES que usar transacciones JTA (vamos programadores a pasar por la tabla de EJB3, no os preocupéis de los tiburones que veis ahí abajo!), si usas Spring puedes usarlo usando los mecanismos de transacciones de Spring (que puede usar JTA o no). Osea que si quieres esa comodidad en Hibernate 3 a usar un servidor de aplicaciones (¿te he dicho ya que hibernate pertenece al stack de aplicaciones de Jboss, y que Spring rechazó la oferta de pertenecer a ella?), en cambio Spring te deja la libertad de usarlo dentro o fuera de un servidor de aplicaciones.
    Ahora que cada uno tenga su opinión, que yo ya he escrito demasiado ladrillo. En un post siguiente mis problemas con aquellas librerías que usan Hibernate por debajo, y si lo consigo ;-) cómo lo soluciono.

    Salu2



    Close
    E-mail It