02
Jun

Transacciones y bloqueos en Glassfish v3 con EJB3.1 y JPA2 (III)

jpa_ejb3_3Tercera entrega y última, por el momento, del megatutorial sobre transacciones y bloqueos con EJB3.1 y JPA2 sobre Glassfish v3.

Mientras que en la primera entrega dejamos configurado el servidor y en la segunda configuramos el proyecto en Netbeans, en esta estudiaremos directamente el código que nos permitirá trabajar con transacciones y bloqueos.

Nuestro proyecto web va a ser más simple que el asa de un cubo. Constará de un servlet escrito “a pelo Capello” denominado RenombraServlet, un EJB Stateless llamado ActualizaCliente , dos EJBs de sesión tan míseros que no merecen ni ser mencionados, aunque nos ayudaran a probar como conseguir transacciones y bloqueos pesimistas, y optimistas con EJB3.1; y, por último, dos EJBs de entidad – Customer y Order- que facilitan el mapeo entre la base de datos y nuestra aplicación.

¿Qué hace nuestra aplicación? Sencillo. Cuando se invoque el servlet se buscará al cliente con ID = 1. Si no existe, nos crearemos un cliente llamado “José María Arranz”, si existe, lo renombraremos dos veces por código aunque podremos comprobar que, en el mejor de los casos, sólo se modificará el nombre del cliente una única vez.

Una vez descritos de forma genérica la aplicación y su funcionamiento, os presentaré las clases una a una y, finalmente, os explicaré cómo se consiguen transacciones y bloqueos con las mismas.

1. EL SERVLET Y EL EJB RENOMBRADOR

La clase RenombraServlet.java es el punto de acceso de la aplicación. Por obra y gracia de la especificación 3.0 de servlets se autoconfigura con una ruta de acceso /RenombraServlet, así que ya sabéis, para acceder a la aplicación vía el servletcito:  http://tuhost:tupuerto/tucontexto/RenombraServlet

Una cosa importante a tener en cuenta es que se puede invocar al método con un parámetro opcional lock que te permitirá hacer renombrados con distintos bloqueos y que os explicaré más adelante.

Así, por ejemplo, si invocáis al servlet con el parámetro lock con valor 1 (http://tuhost:tupuerto/tucontexto/RenombraServlet?lock=1), estaréis renombrando al cliente “José María Arranz” con un bloqueo pesimista con un NOWAIT como una casa de grande.

Básicamente, es un servlet corriente y moliente que escribe HTML directamente en  la HttpServletResponse para ir mostrando los resultados del renombrado o de los errores del mismo por navegador y facilitar la comprobación  del correcto funcionamiento de la aplicación.

Llama a un EJB llamado Renombrador.java que es una clase absurda. lamentable y claramente prescindible pero, que he creado para que veáis que un EJB puede ser cualquier cosa a la que le claves la anotación correspondiente. Renombrador se limitará a devolver un nuevo nombre para el cliente que estará compuesto con los milisegundos de la fecha del sistema (sí, lo di todo por destacar al menos en originalidad).

Una vez que haya obtenido el nuevo nombre para el cliente, se invocará el método renombra del EJB ActualizaCliente.java que, sorprenderos, actualizará el nombre del cliente.

[gist id="421028" nometa="true"]

El sofisticadísimo renombrador:

[gist id="421063" nometa="true"]

2. EL EJB ACTUALIZADOR: BLOQUEOS

El EJB ActualizaCliente.java contiene todo lo que necesitáis saber para crear bloqueos optimistas, pesimistas con NOWAIT y pesimistas con un WAIT determinado.

El método principal es renombra(String nombre, int tipoBloqueo) y, a partir de ahí, podéis ver todo el proceso.

Lo primero que se hace en el método es invocar al método secundario getQuery(int tipoBloqueo) que devolverá una Query con el tipo de bloqueo seleccionado:

  • Si habéis invocado a RenombraServlet.java sin parámetros o con el parámetro lock con valor 0, estaréis creando una Query con bloqueos optimistas (te deja consultar una tupla de una tabla de base de datos y sólo te provocará error si actualizas esa tupla después de que haya sido actualizada previamente por otra transacción entre el momento en el que consultaste la tupla y actualizaste -error en em.flush()-)
  • Si habéis invocado a RenombraServlet.java con el parámetro lock con valor 1, estaréis creando una Query con bloqueos pesimistas NO WAIT o “sin espera” (sólo permite que una transacción consulte una tupla determinada de una tabla de base de datos y lanzará error si intentas consultar la misma tupla antes de que acabe la primera transacción -error en q.getSingleResult()-)
  • Si habéis invocado a RenombraServlet.java con el parámetro lock con valor 2, estaréis creando una Query con bloqueos pesimistas WAIT o “con espera” (sólo permite que una transacción consulte una tupla determinada de una tabla de base de datos y lanzará error si intentas consultar la misma tupla antes de que acabe la primera transacción y en  n segundos no finaliza la misma -error en q.getSingleResult()-). En esta aplicación, la espera o WAIT está configurada para durar dos segundos.

Podéis probar los bloqueos depurando la aplicación y accediendo a la misma desde dos navegadores. Si ponéis un punto de interrupción justo en la línea donde se invoca q.getSingleResult(), con un primer hilo invocáis el método y pausáis la aplicación, cuando hagáis lo mismo con el segundo hilo veréis como la aplicación lanza un error si habéis utilizado un bloqueo pesimista -inmediatamente si es de tipo NOWAIT y después de n segundos en el caso del WAIT- y como continúa en el caso de que utilicéis los bloqueos optimistas.

Poned otro punto de interrupción en la línea em.flush(), veréis que el hilo de bloqueo pesimista que consiga llegar hasta dicho punto lo superará sin problemas. Por el contrario, en el caso de los bloqueos optimistas, el primer hilo que invoque el método, persistirá sin problemas y el segundo lanzará un OptimisticLockException que os dejará sentados en la silla con cara de asno esloveno.

[gist id="421067" nometa="true"]

3. EL EJB NO ACTUALIZADOR: TRANSACCIONES

Si con la clase ActualizaCliente.java hemos aprendido como se utilizan bloqueos de todo tipo con EJB3.1 y el comportamiento de cada uno de los mismos, con el EJB NOActualizaCliente.java comprenderemos cómo se manejan transacciones a través de invocaciones e invocaciones de EJBs.

Los EJBs nos proporcionan una transaccionalidad inmediata en cada uno de sus métodos, sin embargo, puede que tengamos lógica de negocio que necesite ser invocada con transaccionalidad dispersada por varias clases y métodos. ¿Cómo podemos conservar nuestras preciosas transacciones ACID a través de toda la maraña de invocaciones?

La sencillez de gestión de transacciones con JPA es inversamente proporcional a la corrección con que la gente las utiliza.

La clase NOActualizaCliente.java se llama así porque realmente jamás actualizará la base de datos. ¿Por qué, si se modifica un objeto mapeado directamente con una tupla en una tabla, y se persiste mediante em.persist() y em.flush()? Pues muy sencillo: por la configuración de transacciones dentro de la clase.

Si os fijáis bien, la clase se declara con una configuración global de transacciones:

@TransactionAttribute(TransactionAttributeType.REQUIRED)

Esta configuración se aplica a TODOS los métodos de la clase y, básicamente implica dos cosas:

  • Si el método se invoca dentro de una transacción, se utiliza dicha transacción
  • Si el método no se invoca dentro de una transacción, se crea una que se cerrará con la finalización del mismo

Sin embargo, puede que queramos que nuestro método tenga una configuración de transaccionalidad especifica y diferente del resto de los métodos de la clase ¿Cómo podemos conseguirlo? Proporcionando dicha configuración especifica mediante la misma anotación, pero a nivel de método:

 @TransactionAttribute(TransactionAttributeType.MANDATORY)

La configuración de transaccionalidad MANDATORY es ligeramente distinta a la de REQUIRED porque esta configuración implica que el método debe ser invocado dentro de una transacción y, si no es así, se lanza un error.  Si intentaramos invocar este método directamente desde nuestro Servlet, el error que obtendrías sería de proporciones bíblicas.

¿Quiere decir esto que no podremos invocarlo jamás desde un servlet? Pues… no. Podemos crear transacciones de forma manual, aunque seguro, seguro, seguro que el contenedor las gestiona mejor que nosotros… o al menos que yo.

Al utilizar la configuración MANDATORY me he asegurado que la lógica de este método siempre sea invocada dentro de una transacción. Como el método renombra() de NOActualizaCliente.java se invoca desde el método renombra() de la clase ActualizaCliente.java e, inmediatamente después, se vuelve a actualizar desde ese método el objeto actualizado por el primero, la actualización de la clase NOActualizaCliente.java jamás se hará efectiva en la base de datos.

¿Por qué? Porque estamos dentro de una transacción y, hasta que la misma no finalice, no se harán efectivos ninguno de los cambios realizados a nivel de código. La finalización del método renombra() de NOActualizaCliente.java no implica la finalización de ninguna transacción y, por tanto, no implica persistencia de los datos.

[gist id="421069" nometa="true"]

Para finalizar, os copio los EJBs de tipo Entity que soportan el mapeo entre los objetos de la aplicación y la base de datos.

Customer:

[gist id="421145" nometa="true"]

Order:

[gist id="421151" nometa="true"]

Espero que después de este tutorial de todo a cien, al menos tengáis claro que SE PUEDEN conseguir bloqueos de todo tipo y transacciones ACID con EJB3.1 y JPA2 (recordar, JPA1 sólo soportaba las optimistas). Ahora tenéis el poder suficiente para hacer lo que os dé la gana con vuestras transacciones y bloqueos; y, como decía el filósofo más importante del siglo XX, el Tío Ben de Peter Parker/Spiderman “un gran poder implica una gran responsabilidad”. Espero que todo esto os sea útil y os haya ahorrado algunas horas de I+D.

Peter, no la cagues con las transacciones…

Imagen de previsualización de YouTube

DISCLAIMER:

  • Ningún gatito resultó herido en la realización de este tutorial
  • Debo agradecer a Alberto Peña (aka @plagelao) el conocimiento, uso y disfrute de los Gist de de GitHub con los que se comparte el código de este ejemplo.
  • Si quieres el código de esta aplicación y todos sus ficheros de configuración, mándame un correo y por un módico precio llegaremos a un acuerdo. Si lo pides por favor, puede que te salga gratis (gracias a Sixservix)

Artículos relacionados:

  1. Arquitectura de logging en Glassfish Aprende como funciona el sistema de registro o logging en...
  2. Transacciones y bloqueos en Glassfish v3 con EJB3.1 y JPA2 (II) Segunda parte del tutorial sobre como utilizar transacciones y bloqueos...
  3. Transacciones y bloqueos en Glassfish v3 con EJB3.1 y JPA2 (I) Un sencillo tutorial sobre como utilizar transacciones y bloqueos con...


free blog themes
free blog themes