Mostrando entradas con la etiqueta JPA. Mostrar todas las entradas
Mostrando entradas con la etiqueta JPA. Mostrar todas las entradas

lunes, 25 de julio de 2011

EJB JPA - Ejemplos para descargar

A continuación se podrán descargar los ejemplos de EJB - JPA con transacciones que se vieron a lo largo  de los siguientes capítulos de este blog:

Descargas de ejemplos por  modulo (web, ejb y dominio)

Para levantar un entorno de trabajo con Eclipse + EJB3 + JPA + Hibernate para poder ejecutar estos ejemplos, ver: Tutorial JPA - Configuración Eclipse + JBoss + EJB3 + JPA + Hibernate
...

Asi nos deberia quedar el eclipse con el codigo fuente bajado e importado:








jueves, 14 de julio de 2011

JPA - Configuración Eclipse + JBoss + EJB3 + JPA + Hibernate

Este tutorial pretende explicar paso a paso como configurar un entorno de trabajo para empezar a trabajar con JBoss, EJB3, JPA, Hibernate y Eclipse.

Al final de este tutorial, encontrara los ejemplos de las entidades, EJBs,  servlets y datasource que se vieron a lo largo de todo este capitulo, listos para descargar.. 

Requisitos previos
  • Tener instalado y levantado una base de datos Oracle 10g/11g
  • Usuario / password de base de datos  
    • hibernate_tutorial / hibernate_tutorial 
  • Comandos para crear el usuario y password en la BD, necesarios para este tutorial:
    • drop user hibernate_tutorial cascade;
    • create user hibernate_tutorial identified by hibernate_tutorial;
    • grant DBA to hibernate_tutorial;

Descargas:
  • Descargar las librerias de Hibernate (en D:\APIS)
  • Descargar las librerias de OpenEjb (en D:\APIS)

Pasos para la configuración del entorno de desarrollo en el eclipse
  • Procedemos a crear un proyecto java simple (lo llamaremos ModelTutorial). Este proyecto/modulo tendra las entidades que luego los modulos EJB y Web podrán acceder.


  • Una vez creado el proyecto java, procedemos a configurar las librerías con las anotaciones de Hibernate, JPA y el driver de oracle. Todas estas librerias seran tambien de gran utilidad en caso que querramos hacer los test de las entidades creadas.



  • Agregar los JARs de Hibernate

  • Agregar los JARs a la libreria de usuario recientemente creada
    • Agregar todos los jars de D:\APIS\Hibernate\hibernate-distribution-3.3.1.GA\lib\required (menos el slf4j-api-1.5.2.jar) 
    • Agregar el jar de D:\APIS\Hibernate\hibernate-distribution-3.3.1.GA\hibernate3.jar
    • Agregar el jar de D:\APIS\Hibernate\hibernate-annotations-3.4.0.GA\hibernate-annotations.jar
    • Agregar el jar de D:\APIS\Hibernate\hibernate-annotations-3.4.0.GA\lib\hibernate-commons-annotations.jar
    • Agregar el jar de D:\APIS\Hibernate\hibernate-entitymanager-3.4.0.GA\hibernate-entitymanager.jar
    • Agregar el jar de D:\APIS\log4j\apache-log4j-1.2.16\log4j-1.2.16.jar
    • Agregar el jar de  D:\APIS\openejb\openejb-3.1.4\lib\javaee-api-5.0-3.jar
    • Agregar el jar de  D:\APIS\slf4j-1.5.8\slf4j-api-1.5.8.jar
    • Agregar el jar de  D:\APIS\slf4j-1.5.8\slf4j-simple-1.5.8.jar


  • Agregar el JAR del driver de ORACLE

  • Agregar el JAR de JUnit (que viene con el eclipse)




  • Mostramos todos los JARs agregados a este proyecto/modulo

  • Ahora procedemos a crear una entidad a modo de ejemplo (se llamara Referee)

  • Creamos el persistence.xml en el modulo/proyecto de EJB (EjbTutorial) dentro de la carpeta META-INF como muestra la siguiente captura  IMPORTANTE: No colocar el persistence.xml dentro del modulo/proyecto ModelTutorial ya que cuando vamos a querer inyectar el persistenceContext (EntityManager) en el EJB nos va a tirar un error que no encuentra el mismo. En el unico lugar que debería ir es en el modulo de EJB.
  • Ahora vamos a configurar la dependencia entre el modulo EJB (EjbTutorial) con el modulo de dominio (ModelTutorial).







<persistence
 xmlns="http://java.sun.com/xml/ns/persistence"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://java.sun.com/xml/ns/persistence 
 http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd"
 version="1.0">

    <persistence-unit name="examplePersistenceUnit" transaction-type="JTA">    
      <provider>org.hibernate.ejb.HibernatePersistence</provider>    
      <jta-data-source>java:/TutorialDS</jta-data-source>


      <!-- Oracle DB -->
        <properties>
          <property name="hibernate.show_sql" value="true" />
            <property name="hibernate.format_sql" value="true" />
            <property name="hibernate.dialect"
                      value="org.hibernate.dialect.Oracle9Dialect"/>  
<property name="hibernate.hbm2ddl.auto" value="create-drop"/>
</properties>              
    </persistence-unit>
</persistence>

  • Ahora procedemos a crear el datasource (con todos los datos de conectividad a la BD). Lo llamaremos oracle-ds.xml
<?xml version="1.0" encoding="UTF-8"?>
<datasources>
    <local-tx-datasource>
        <jndi-name>TutorialDS</jndi-name>
        <connection-url>jdbc:oracle:thin:@localhost:1521:xe</connection-url>
        <driver-class>oracle.jdbc.driver.OracleDriver</driver-class>
        <user-name>hibernate_tutorial</user-name>
        <password>hibernate_tutorial</password>
        <valid-connection-checker-class-name>
              org.jboss.resource.adapter.jdbc.vendor.
              OracleValidConnectionChecker
        </valid-connection-checker-class-name>     
    </local-tx-datasource>
</datasources>

  • Este datasource lo vamos a colocar en D:\servers\jboss-5.1.0.GA\server\default\deploy

  • Y por ultimo, vamos a colocar el JAR del driver de oracle (el mismo que habiamos agregado al modulo ModelTutorial) en el siguiente directorio del JBoss: D:\servers\jboss-5.1.0.GA\server\default\lib


Descargas de los fuentes

Para descargar los fuentes del modulo EjbTutorial, haga clic aqui
Para descargar los fuentes del modulo ModelTutorial, haga clic aqui
Para descargar los fuentes del modulo WebEjbTest, haga clic aqui
Para descargar los fuentes del datasource, haga clic aqui

Sitios similares

* http://schuchert.wikispaces.com/EJB+3+and+Java+Persistence+API



    jueves, 7 de julio de 2011

    JPA VII - Transacciones

    Este tutorial pretende explicar en detalle el tema de las transacciones en cualquier aplicación J2EE.
    Como vimos en el tutorial anterior (ver JPA VI - EntityManager) para las situaciones en donde no requerimos los servicios ofrecidos por un EJB3 Container (como por ejemplo JBoss ó WebLogic application server) pero querremos utilizar el modelo de persistencia de JPA, el API JPA provee el Application-managed entity manager donde el ciclo de vida de una transacción es manejada por la aplicación (es decir, el desarrollador iniciara y terminara la transacción).
    En este tutorial, vamos a entrar en detalle en aquellas situaciones en donde si se requiera de los servicios de un EJB3 container, para esto el API JPA provee el Container-managed entity manager. 

    Al final del capitulo, encontraremos el código fuente para descargar los ejemplos que se vean a lo largo del mismo.

    Las transacciones manejadas por el contenedor usan el API de java conocida como JTA.

    " Typically, the container begins a transaction immediately before an enterprise bean method starts. It commits the transaction just before the method exits. Each method can be associated with a single transaction. Nested or multiple transactions are not allowed within a method.
    Container-managed transactions do not require all methods to be associated with transactions. When deploying a bean, you specify which of the bean's methods are associated with transactions by setting the transaction attributes."
    Temas a tratar:
    • Propiedades de las transacciones
    • Atributos de las transacciones
      • Required
      • Required new
      • Supports
      • Not Supported
      • Mandatory
      • Never
    • Ejemplos de como utilizar los atributos de las transacciones
    • Interface SessionSynchronization (3 metodos callback)
      • afterBegin
      • beforeCompletion
      • afterCompletion
    • Doomed Transactions
    • Concurrencia y bloqueos de base de datos (database locking
      • Problemas de las transacciones concurrentes y los niveles de aislamiento
      • Pessimistic locking & Optimistic locking (@Version)
    • BMT - Transacciones demarcadas por el Bean

    ■  Propiedades de las transacciones

    Atomicidad: Todos las tareas que se llevan a cabo dentro de una misma transacciones, o bien terminan con éxito  o bien ninguna termina. Todas las tareas son vistas como una solo unidad atómica. Es decir, esta propiedad no asegura que ante un fallo del sistema no puede quedar a medias.

    Consistencia: Una transacción terminara comiteando los valores y dejando en un estado valido a la BD o  bien hará rollback dejando la BD en el estado previo a la ejecución de la transacción (con todos sus pasos o tareas)

    Aislamiento: Todos los cambios que se ejecuten contra la BD en una misma transacción no serán visibles por otras transacciones hasta que dichos cambio se comiteen. Es decir, nos segura que una operación no puede afectar a otras.

    Durabilidad: Esta propiedad asegura que una vez realizada la operación, ésta persistirá y no se podrá deshacer aunque falle el sistema.

    Existen diferentes grados de aislamiento. el máximo nivel de aislamiento no puede ser la más conveniente ya que puede haber una penalización de rendimiento muy alto a pagar.

    Cuando el contenedor EJB se encarga de manejar las transacciones, tenemos que decidir cuando es que comenzara y terminara la transaccion, para que esta sea manejada por el conentedor (JBoss o WebLogic entre otros)
    Estas políticas de demarcación son definidas por diferentes atributos de una transacción y pueden ser seteados en los métodos o clases de los session beans (EJBs),


    ■  Atributos de las transacciones (Transaction attributes)

    SUPPORTS: Si la llamada se hace desde una transacción activa,  el método con la anotación de dicha propiedad sera ejecutado dentro de la misma transacción activa.
    Si la llamada se hace desde un método que no tiene activa una transacción, el método con la anotación de dicha propiedad sera ejecutado sin la creación de una transacción. Es decir, no se creara una transacción para dicha ejecución.

    NOT_SUPPORTED: Tanto para el caso en el que la llamada se hace desde una transaccion activa como si se hace desde un método que no tiene ninguna transaccion activa, el metodo con la anotación de dicha propiedad sera ejecutado sin ninguna transaccion. Es decir, sera ejecutado fuera del contexto de transacción.

    REQUIRED: Esta es la propiedad por default.  El método con la anotación de dicha propiedad requiere ser ejecutado dentro de una transacción. En caso que la llamada a este método ya haya iniciado la transacción  este método utilizara la misma. En caso que la llamada a este método no haya iniciado una transacción, una nueva transacción se iniciara para la ejecución de este método.

    REQUIRES_NEW: El método con la anotación de dicha propiedad requiere ser ejecutado siempre en una nueva transaccion independientemente si ya exista o no una transaccion en el contexto. En caso que ya exista una transacción activa, la transacción original sera reanudada luego de que la nueva transacción efectue el commit o rollback pertinente, 

    MANDATORY: El método con la anotación de dicha propiedad requiere que el método que llame a este método ya haya iniciado la transacción. Es decir, cuando se llame al este método ya tiene que existir en el contexto una transacción activa. En caso contrario se lanzara una expectación.

    NEVER: El método con la anotación de dicha propiedad requiere que el método que llame a este método no ya haya iniciado ninguna transacción. En caso que este método encuentre una transacción activa, lanzara una excepción.


    Transaction Attribute
    Caller Transaction Exists
    Effect
    REQUIRED
    No
    Container creates a new transaction
    Yes
    Method joins the callers transaction
    REQUIRES_NEW
    No
    Container creates a new transaction
    Yes
    Container creates a new transaction and the callers transaction is suspended
    SUPPORTS
    No
    No transaction is used
    Yes
    Method joins the callers transaction
    MANDATORY
    No
    javax.ejb.EJBTransactionRequiredException is thrown
    Yes
    Method joins the callers transaction
    NOT_SUPPORTED
    No
    No transaction is used
    Yes
    The callers transaction is suspended and the method is called without a transaction
    NEVER
    No
    No transaction is used
    Yes
    javax.ejb.EJBException is thrown

    Tabla extraída de la siguiente URL: http://www.datadisk.co.uk/html_docs/ejb/ejb3_transactions.htm


    ■  Ejemplos de los diferentes atributos de transacciones


    Tx: Sinónimo de transacción
    Los atributos de las transacciones vamos a poder definirlos a nivel de clase (EJB) o a nivel de los métodos:

      • En caso de estar definido a nivel de clase, todos los métodos serán ejecutados bajo dicha definición salvo aquellos métodos a los cuales se les defina un atributo de transacción distinto
      • El atributo por defecto definido a nivel de clase es el de REQUIRED. Es decir, si tenemos un bean que tiene 3 métodos y solo a un método se le define un atributo de transacción distinto al REQUIRED, este método sera ejecutado bajo esta nueva definición mientras que los otros dos, serán ejecutados bajo la definición de REQUIRED

    Los siguientes ejemplos son lanzados desde un SERVLET llamado ECommerceTest el cual llamara a un EJB llamado ECommerceBean y en varias oportunidades, este a su vez llamara a otro EJB llamado AuditServiceBean

    1.- Required Tx

    Servlet ECommerceTest

    private void requiredTransactionAttributTest() {
    System.out.println("***** requiredTransactionAttributTest INICIO ******* ");
    eCommerceBeanLocal.saveRefereeAndAuditMsgAndRequiredTxAndThrowException(102, "Prueba2");
    System.out.println("***** requiredTransactionAttributTest FIN ******* ");
    }


    EJB ECommerceBean

    public void saveRefereeAndAuditMsgAndRequiredTxAndThrowException(int id, String name) {
    System.out.println("*********** saveRefereeAndAuditMsgAndRequiredTxAndThrowException INICIO *************");
    Referee r1 = new Referee();
    r1.setName(name);
    r1.setId(id);
    em.persist(r1);
    auditServiceBean.addAuditMessageWithTransactionRequired(id, name + " [AUDITORIA] ");
    throw new RuntimeException("add customer - simulated system failure");
    }

    EJB AuditServiceBean

    @TransactionAttribute(TransactionAttributeType.REQUIRED)
    public void addAuditMessageWithTransactionRequired(int id, String name) {
    System.out.println("***** addAuditMessageWithTransactionRequired INICIO ******");
    AuditMessage am = new AuditMessage();
    am.setId(id);
    am.setName(name);
    em.persist(am);
    System.out.println("***** addAuditMessageWithTransactionRequired FIN ******");
    }


    La traza del log es la siguiente:

    18:14:44,546 INFO  [STDOUT] ***** requiredTransactionAttributTest INICIO ******* 
    18:14:44,625 WARN  [InterceptorsFactory] EJBTHREE-1246: Do not use InterceptorsFactory with a ManagedObjectAdvisor, InterceptorRegistry should be used via the bean container
    18:14:44,625 WARN  [InterceptorsFactory] EJBTHREE-1246: Do not use InterceptorsFactory with a ManagedObjectAdvisor, InterceptorRegistry should be used via the bean container
    18:14:44,625 INFO  [STDOUT] *********** saveRefereeAndAuditMsgAndRequiredTxAndThrowException INICIO *************
    18:14:44,859 WARN  [InterceptorsFactory] EJBTHREE-1246: Do not use InterceptorsFactory with a ManagedObjectAdvisor, InterceptorRegistry should be used via the bean container
    18:14:44,859 WARN  [InterceptorsFactory] EJBTHREE-1246: Do not use InterceptorsFactory with a ManagedObjectAdvisor, InterceptorRegistry should be used via the bean container
    18:14:44,859 INFO  [STDOUT] ***** addAuditMessageWithTransactionRequired INICIO ******
    18:14:44,859 INFO  [STDOUT] ***** addAuditMessageWithTransactionRequired FIN ******
    18:14:44,890 ERROR [[ECommerceTest]] Servlet.service() for servlet ECommerceTest threw exception
    javax.ejb.EJBException: java.lang.RuntimeException: add customer - simulated system failure
    at org.jboss.ejb3.tx.Ejb3TxPolicy.handleExceptionInOurTx(Ejb3TxPolicy.java:77)
    at org.jboss.aspects.tx.TxPolicy.invokeInOurTx(TxPolicy.java:83)


    Se lanza un error de manera intencional para ver como es que este atributo funciona. Aquí podemos ver que tanto, toda la lógica del método que llama como la del  que es llamado, se hace rollback y no se persiste ningún cambio. Esto se debe a que el método addAuditMessageWithTransactionRequired() es ejecutado dentro de la misma transacción que el metodo que lo llamo
    (saveRefereeAndAuditMsgAndRequiredTxAndThrowException()).

    Las tablas de la BD nos quedaran de la siguiente manera

    Tabla Referee:
     ID     COMMENTS     NAME    
     -----  -----------  ------- 

    Tabla AuditMessage:
     ID     COMMENTS     NAME                 
     -----  -----------  -------------------- 

    2.- Required New Tx

    Servlet ECommerceTest

    private void requiredNewTransactionAttributTest() {
    System.out.println("***** requiredNewTransactionAttributTest INICIO ******* ");
    eCommerceBeanLocal.saveRefereeAndAuditMsgAndRequiredNewTxAndThrowException(103, "Prueba3");
    System.out.println("***** requiredNewTransactionAttributTest FIN ******* ");
    }

    EJB ECommerceBean

    public void saveRefereeAndAuditMsgAndRequiredNewTxAndThrowException(int id, String name) {
    System.out.println("*********** saveRefereeAndAuditMsgAndRequiredNewTxAndThrowException INICIO *************");
    Referee r1 = new Referee();
    r1.setName(name);
    r1.setId(id);
    em.persist(r1);
    auditServiceBean.addAuditMessageWithTransactionRequired_New(id, name + " [AUDITORIA] ");
    throw new RuntimeException("add customer - simulated system failure");
    }

    EJB AuditServiceBean

    @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
    public void addAuditMessageWithTransactionRequired_New(int id, String name) {
    System.out.println("***** addAuditMessageWithTransactionRequired_New INICIO ******");
    AuditMessage am = new AuditMessage();
    am.setId(id);
    am.setName(name);
    em.persist(am);
    System.out.println("***** addAuditMessageWithTransactionRequired_New FIN ******");
    }

    La traza del log es la siguiente:

    18:36:36,203 INFO  [STDOUT] ***** requiredNewTransactionAttributTest INICIO ******* 
    18:36:36,203 WARN  [InterceptorsFactory] EJBTHREE-1246: Do not use InterceptorsFactory with a ManagedObjectAdvisor, InterceptorRegistry should be used via the bean container
    18:36:36,203 WARN  [InterceptorsFactory] EJBTHREE-1246: Do not use InterceptorsFactory with a ManagedObjectAdvisor, InterceptorRegistry should be used via the bean container
    18:36:36,203 INFO  [STDOUT] *********** saveRefereeAndAuditMsgAndRequiredNewTxAndThrowException INICIO *************
    18:36:36,234 WARN  [InterceptorsFactory] EJBTHREE-1246: Do not use InterceptorsFactory with a ManagedObjectAdvisor, InterceptorRegistry should be used via the bean container
    18:36:36,234 WARN  [InterceptorsFactory] EJBTHREE-1246: Do not use InterceptorsFactory with a ManagedObjectAdvisor, InterceptorRegistry should be used via the bean container
    18:36:36,234 INFO  [STDOUT] ***** addAuditMessageWithTransactionRequired_New INICIO ******
    18:36:36,234 INFO  [STDOUT] ***** addAuditMessageWithTransactionRequired_New FIN ******
    18:36:36,312 INFO  [STDOUT] Hibernate: 
        insert 
        into
            AuditMessage
            (comments, name, id) 
        values
            (?, ?, ?)
    18:36:37,031 ERROR [[ECommerceTest]] Servlet.service() for servlet ECommerceTest threw exception
    javax.ejb.EJBException: java.lang.RuntimeException: add customer - simulated system failure
    at org.jboss.ejb3.tx.Ejb3TxPolicy.handleExceptionInOurTx(Ejb3TxPolicy.java:77)
    at org.jboss.aspects.tx.TxPolicy.invokeInOurTx(TxPolicy.java:83)

    Se lanza un error de manera intencional para ver como es que este atributo funciona. Aquí podemos ver que la lógica del método que llama
    (saveRefereeAndAuditMsgAndRequiredTxAndThrowException())  se hara rollback mientras que la lógica del método llamado 
    (addAuditMessageWithTransactionRequired()) persistira los cambios. Es decir, la BD nos quedara de la siguiente manera

    Tabla Referee:
     ID     COMMENTS     NAME    
     -----  -----------  ------- 

    Tabla AuditMessage:
     ID     COMMENTS     NAME                 
     -----  -----------  -------------------- 
     103    (null)       Prueba3 [AUDITORIA]  

    3.- Not Supported Tx

    Servlet ECommerceTest

    private void notSupportedTransactionAttributTest() {
    System.out.println("***** notSupportedTransactionAttributTest INICIO ******* ");
    eCommerceBeanLocal.saveRefereeAndAuditMsgAndNotSupportedTxAndThrowException(104, "Prueba4");
    System.out.println("***** notSupportedTransactionAttributTest FIN ******* ");
    }

    EJB ECommerceBean

    public void saveRefereeAndAuditMsgAndNotSupportedTxAndThrowException(int id, String name) {
    System.out.println("*********** saveRefereeAndAuditMsgAndNotSupportedTxAndThrowException INICIO *************");
    Referee r1 = new Referee();
    r1.setName(name);
    r1.setId(id);
    em.persist(r1);
    auditServiceBean.addAuditMessageWithTransactionNotSupported(id, name + " [AUDITORIA] ");
    }

    EJB AuditServiceBean

    @TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
    public void addAuditMessageWithTransactionNotSupported(int id, String name) {
    System.out.println("***** addAuditMessageWithTransactionNotSupported INICIO ******");
    System.out.println("No interactuamos con BD. Logueamos en una Archivo " + id + " - " + name);
    }

    La traza del log es la siguiente:

    19:00:39,500 INFO  [STDOUT] ***** notSupportedTransactionAttributTest INICIO ******* 
    19:00:39,515 WARN  [InterceptorsFactory] EJBTHREE-1246: Do not use InterceptorsFactory with a ManagedObjectAdvisor, InterceptorRegistry should be used via the bean container
    19:00:39,515 WARN  [InterceptorsFactory] EJBTHREE-1246: Do not use InterceptorsFactory with a ManagedObjectAdvisor, InterceptorRegistry should be used via the bean container
    19:00:39,515 INFO  [STDOUT] *********** saveRefereeAndAuditMsgAndNotSupportedTxAndThrowException INICIO *************
    19:00:39,531 WARN  [InterceptorsFactory] EJBTHREE-1246: Do not use InterceptorsFactory with a ManagedObjectAdvisor, InterceptorRegistry should be used via the bean container
    19:00:39,531 WARN  [InterceptorsFactory] EJBTHREE-1246: Do not use InterceptorsFactory with a ManagedObjectAdvisor, InterceptorRegistry should be used via the bean container
    19:00:39,531 INFO  [STDOUT] ***** addAuditMessageWithTransactionNotSupported INICIO ******
    19:00:39,531 INFO  [STDOUT] No interactuamos con BD. Logueamos en una Archivo 104 - Prueba4 [AUDITORIA] 
    19:00:39,546 INFO  [STDOUT] Hibernate: 
        insert 
        into
            Referee
            (comments, name, id) 
        values
            (?, ?, ?)
    19:00:39,890 INFO  [STDOUT] ***** notSupportedTransactionAttributTest FIN ******* 

    Aquí podemos ver como se le agrega el atributo de NOT_SUPPORTED al método addAuditMessageWithTransactionNotSupported() ya que dicho método no necesitara interactuar con la BD (contexto de persistencia). Por ejemplo, puede que este método interactue directamente con un fichero en el filesystem.

    IMPORTANTE: Se estuvo probando de lanzar una excepción en el metodo  addAuditMessageWithTransactionNotSupported() que no se ejecuta dentro de una   transacción y vimos que NO se graba el objeto  referee independientemente de que este, esté en una transacción independiente. Esto también pasa si agregamos el atributo REQUIRED_NEW a dicho metodo (en lugar del atributo NOT_SUPPORTED). Es decir, si agregamos una excepción en el método llamado (método del 2do EJB) es como que corta la ejecución y no persistirá las entidades que tendría que persistir del método llamador (método del 1er EJB).

    Tabla Referee:
     ID     COMMENTS     NAME    
     -----  -----------  ------- 
     104    (null)       Prueba4 

    Tabla AuditMessage:
     ID     COMMENTS     NAME                 
     -----  -----------  -------------------- 

    4.- Supported Tx (veremos dos ejemplos)

    Servlet ECommerceTest

    private void supportsTransactionAttributTest() {
    System.out.println("***** supportsTransactionAttributTest INICIO ******* ");
    eCommerceBeanLocal.saveRefereeAndSupportsTx(105, "Prueba5");
    System.out.println("***** supportsTransactionAttributTest FIN ******* ");
    }

    EJB ECommerceBean

    @TransactionAttribute(TransactionAttributeType.SUPPORTS)
    public void saveRefereeAndSupportsTx(int id, String name) {
    System.out.println("*********** saveRefereeAndSupportsTx INICIO *************");
    Referee r1 = new Referee();
    r1.setName(name);
    r1.setId(id);
    em.persist(r1);
    }

    La traza del log es la siguiente:

    19:30:16,484 INFO  [STDOUT] ***** supportsTransactionAttributTest INICIO ******* 
    19:30:16,484 WARN  [InterceptorsFactory] EJBTHREE-1246: Do not use InterceptorsFactory with a ManagedObjectAdvisor, InterceptorRegistry should be used via the bean container
    19:30:16,484 WARN  [InterceptorsFactory] EJBTHREE-1246: Do not use InterceptorsFactory with a ManagedObjectAdvisor, InterceptorRegistry should be used via the bean container
    19:30:16,484 INFO  [STDOUT] *********** saveRefereeAndSupportsTx INICIO *************
    19:30:16,500 ERROR [[ECommerceTest]] Servlet.service() for servlet ECommerceTest threw exception
    javax.persistence.TransactionRequiredException: EntityManager must be access within a transaction
    at org.jboss.jpa.deployment.ManagedEntityManagerFactory.verifyInTx(ManagedEntityManagerFactory.java:155)
    at org.jboss.jpa.tx.TransactionScopedEntityManager.persist(TransactionScopedEntityManager.java:186)

    Aquí podemos ver como se lanzara una excepción ya que se intenta persistir un referee en la BD sin una transacción inicializada.

    Ahora veamos el 2do ejemplo para este atributo:

    Servlet ECommerceTest

    private void supportsTransactionAttributTest2() {
    System.out.println("***** supportsTransactionAttributTest INICIO ******* ");
    eCommerceBeanLocal.saveRefereeAndSupportsTx2(105, "Prueba5");
    System.out.println("***** supportsTransactionAttributTest FIN ******* ");
    }

    EJB ECommerceBean

    @TransactionAttribute(TransactionAttributeType.REQUIRED)
    public void saveRefereeAndSupportsTx2(int id, String name) {
    System.out.println("*********** saveRefereeAndSupportsTx2 INICIO *************");
    Referee r1 = new Referee();
    r1.setName(name);
    r1.setId(id);
    em.persist(r1);
    auditServiceBean.addAuditMessageWithTransactionSupports(id, name + " [AUDITORIA] ");
    System.out.println("*********** saveRefereeAndSupportsTx2 FIN *************");
    }

    EJB AuditServiceBean

    @TransactionAttribute(TransactionAttributeType.SUPPORTS)
    public void addAuditMessageWithTransactionSupports(int id, String name) { System.out.println("***** addAuditMessageWithTransactionSupports INICIO ******");
    AuditMessage am = new AuditMessage();
    am.setId(id);
    am.setName(name);
    em.persist(am);
    System.out.println("***** addAuditMessageWithTransactionSupports FIN ******");
    }

    La traza del log es la siguiente:

    19:34:52,468 INFO  [STDOUT] ***** supportsTransactionAttributTest INICIO ******* 
    19:34:52,484 WARN  [InterceptorsFactory] EJBTHREE-1246: Do not use InterceptorsFactory with a ManagedObjectAdvisor, InterceptorRegistry should be used via the bean container
    19:34:52,484 WARN  [InterceptorsFactory] EJBTHREE-1246: Do not use InterceptorsFactory with a ManagedObjectAdvisor, InterceptorRegistry should be used via the bean container
    19:34:52,484 INFO  [STDOUT] *********** saveRefereeAndSupportsTx2 INICIO *************
    19:34:52,484 WARN  [InterceptorsFactory] EJBTHREE-1246: Do not use InterceptorsFactory with a ManagedObjectAdvisor, InterceptorRegistry should be used via the bean container
    19:34:52,484 WARN  [InterceptorsFactory] EJBTHREE-1246: Do not use InterceptorsFactory with a ManagedObjectAdvisor, InterceptorRegistry should be used via the bean container
    19:34:52,484 INFO  [STDOUT] ***** addAuditMessageWithTransactionSupports INICIO ******
    19:34:52,484 INFO  [STDOUT] ***** addAuditMessageWithTransactionSupports FIN ******
    19:34:52,484 INFO  [STDOUT] *********** saveRefereeAndSupportsTx2 FIN *************
    19:34:52,484 INFO  [STDOUT] Hibernate: 
        insert 
        into
            Referee
            (comments, name, id) 
        values
            (?, ?, ?)
    19:34:52,546 INFO  [STDOUT] Hibernate: 
        insert 
        into
            AuditMessage
            (comments, name, id) 
        values
            (?, ?, ?)
    19:34:52,562 INFO  [STDOUT] ***** supportsTransactionAttributTest FIN ******* 

    Aquí podemos ver como con la anotación SUPPORTS, se hara un join con la transacción inicializada en el método llamador y por ende, el método llamado (2do método del 2do EJB) podrá llevar a cabo la persistencia en la BD.

    Tabla Referee:
     ID     COMMENTS     NAME    
     -----  -----------  ------- 
     105    (null)       Prueba5 

    Tabla AuditMessage:
     ID     COMMENTS     NAME                 
     -----  -----------  -------------------- 
     105    (null)       Prueba5 [AUDITORIA] 

    5.- Mandatory Tx

    Servlet ECommerceTest

    private void mandatoryTransactionAttributTest() {
    System.out.println("***** mandatoryTransactionAttributTest INICIO ******* ");
    eCommerceBeanLocal.saveRefereeAndMandatoryTx(106, "Prueba6");
    System.out.println("***** mandatoryTransactionAttributTest FIN ******* ");
    }

    EJB ECommerceBean

    @TransactionAttribute(TransactionAttributeType.MANDATORY)
    public void saveRefereeAndMandatoryTx(int id, String name) {
    System.out.println("***** addAuditMessageWithTransactionMandatory INICIO FIN ******");
    }

    La traza del log es la siguiente:

    22:51:12,531 INFO  [STDOUT] ***** mandatoryTransactionAttributTest INICIO ******* 
    22:51:12,546 ERROR [[ECommerceTest]] Servlet.service() for servlet ECommerceTest threw exception
    javax.ejb.EJBTransactionRequiredException: public void ejbmodule.ejb.ECommerceBean.saveRefereeAndMandatoryTx(int,java.lang.String)
    at org.jboss.ejb3.tx.Ejb3TxPolicy.throwMandatory(Ejb3TxPolicy.java:47)
    at org.jboss.ejb3.tx.TxInterceptor$Mandatory.invoke(TxInterceptor.java:141)

    Aquí podemos ver como con esta anotación, el metodo saveRefereeAndMandatoryTx() necesitara que exista una transaccion activa para poder ser ejecutado correctamente. En caso que no exista dicha transacción activa como vimos en el ejemplo, se lanzara la excepción EJBTransactionRequiredException.

    6.- Never Tx

    Servlet ECommerceTest

    private void neverTransactionAttributTest() {
    System.out.println("***** mandatoryTransactionAttributTest2 INICIO ******* ");
    eCommerceBeanLocal.saveRefereeAndNeverTx2(107, "Prueba6");
    System.out.println("***** mandatoryTransactionAttributTest2 FIN ******* ");
    }

    EJB ECommerceBean

    public void saveRefereeAndNeverTx2(int id, String name) {
    System.out.println("*********** saveRefereeAndNeverTx2 INICIO *************");
    Referee r1 = new Referee();
    r1.setName(name);
    r1.setId(id);
    em.persist(r1);
    auditServiceBean.addAuditMessageWithTransactionNever(id, name + " [AUDITORIA] ");
    System.out.println("*********** saveRefereeAndNeverTx2 FIN *************");
    }


    EJB AuditServiceBean


    @TransactionAttribute(TransactionAttributeType.NEVER)
    public void addAuditMessageWithTransactionNever(int id, String name) {
    System.out.println("***** addAuditMessageWithTransactionNever INICIO FIN ******");

    }

    La traza del log es la siguiente:


    23:01:02,140 INFO  [STDOUT] ***** mandatoryTransactionAttributTest2 INICIO ******* 
    23:01:02,140 WARN  [InterceptorsFactory] EJBTHREE-1246: Do not use InterceptorsFactory with a ManagedObjectAdvisor, InterceptorRegistry should be used via the bean container
    23:01:02,140 WARN  [InterceptorsFactory] EJBTHREE-1246: Do not use InterceptorsFactory with a ManagedObjectAdvisor, InterceptorRegistry should be used via the bean container
    23:01:02,156 INFO  [STDOUT] *********** saveRefereeAndNeverTx2 INICIO *************
    23:01:02,156 ERROR [[ECommerceTest]] Servlet.service() for servlet ECommerceTest threw exception
    javax.ejb.EJBException: Transaction present on server in Never call (EJB3 13.6.2.6)
    at org.jboss.ejb3.tx.TxInterceptor$Never.invoke(TxInterceptor.java:61)
    at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:102)

    Aquí podemos ver como con esta anotación, el método
    addAuditMessageWithTransactionNever() necesitara que NO exista una transacción activa para poder ser ejecutado correctamente. En caso, como existe una transaccion debido al metodo que lo llama, se lanzara la excepción EJBException.


    ■  SessionSynchronization Interface

    Existen situaciones en las cuales necesitamos que el application server notifique al EJB cuando las transacciones empiezan o cuando terminan. Los EJB del tipo Stateful pueden recibir este tipo de notificaciones implementando la interface javax.ejb.SessionSynchronization. 
    Dicha interface obligara a implementar 3 metodos que se detallan a continuación:
    • afterBegin: Indica que una transacción esta por comenzar.
    • beforeCompletion: Indica que una transacción esta por finalizar (mediante commit o rollback)
    • afterCompletion: Indica que una transacción ha finalizado (ya sea si se hizo commit o rollback)
    There are occasions when we want the application to have some control over commits and rollbacks in container managed stateful session beans. For example, in a shopping cart application we may decide to cache a running total of purchased items in one transaction and commit them to the database after all items have been purchased. Let's assume that after writing the cache to the database but before committing we decide to rollback the transaction. The database will be rolled backed successfully but the stateful session bean cache, which is non transactional, will still contain the purchased items. We need a mechanism to synchronize the database with the session
    Texto extraído del libro [1] - EJB 3 Developer Guide

    El siguiente ejemplo es lanzado desde un SERVLET llamado ECommerceTest el cual llamara a un EJB llamado ECommerceBean y este a su vez llamara a otro EJB llamado ShoppingCartBean


    Servlet ECommerceTest

    public void doShopping() {
    System.out.println("********** ECommerceTest doShopping INICIO ************");
    eCommerceBeanLocal.doShopping();
    System.out.println("********** ECommerceTest doShopping FIN ************");
    }

    EJB ECommerceBean 

    public void doShopping() {
    System.out.println("********** ECommerceBean doShopping() INICIO *************");
    shoppingCart.addItem("Bread");
    shoppingCart.addItem("Milk");
    shoppingCart.addItem("Tea");
    System.out.println("********** ECommerceBean doShopping() FIN *************");
    }

    EJB ShoppingCartBean

    @Stateful
    public class ShoppingCartBean implements ShoppingCartBeanLocal, SessionSynchronization {


    @PersistenceContext(unitName="examplePersistenceUnit")
    private EntityManager em;
    private Shopping sc;
    private List<String> items;


    @PostConstruct
    public void initialize() {
    items = new ArrayList<String>();
    }

    public void addItem(String item) {
    items.add(item);
    }

    /**
    * Este netodo se ejecuta antes de llamar al primer metodo de la transaccion. 
    */
    public void afterBegin() {

    System.out.println("afterbegin method invoked");
    }

    /**
    * Este netodo se ejecuta antes de ejecutar el commit o rollback
    */
    public void beforeCompletion() {

    System.out.println("beforeCompletion method invoked");
    int itemId = 0;
    for (String item : items) {
    itemId++;
    sc = new Shopping();
    sc.setId(itemId);
    sc.setItem(item);
    em.persist(sc);
    }
    }

    /**
    * Este netodo se ejecuta despues de ejecutar el commit o rollback
    */
    public void afterCompletion(boolean committed) {

    System.out.println("afterCompletion method invoked");
    if (committed == false) {
    // Estos casos ocurre cuando se hizo rollback de la transaccion.
    System.out.println("committed = false");
    items = null;
    }
    }
    }


    La traza del log es la siguiente:

    12:10:02,413 INFO  [STDOUT] ********** ECommerceTest doShopping INICIO ************
    12:10:02,413 WARN  [InterceptorsFactory] EJBTHREE-1246: Do not use InterceptorsFactory with a ManagedObjectAdvisor, InterceptorRegistry should be used via the bean container
    12:10:02,413 WARN  [InterceptorsFactory] EJBTHREE-1246: Do not use InterceptorsFactory with a ManagedObjectAdvisor, InterceptorRegistry should be used via the bean container
    12:10:02,413 INFO  [STDOUT] ********** ECommerceBean doShopping() INICIO *************
    12:10:02,413 INFO  [STDOUT] afterbegin method invoked
    12:10:02,413 WARN  [InterceptorsFactory] EJBTHREE-1246: Do not use InterceptorsFactory with a ManagedObjectAdvisor, InterceptorRegistry should be used via the bean container
    12:10:02,413 WARN  [InterceptorsFactory] EJBTHREE-1246: Do not use InterceptorsFactory with a ManagedObjectAdvisor, InterceptorRegistry should be used via the bean container
    12:10:02,413 INFO  [STDOUT] ********** ECommerceBean doShopping() FIN *************
    12:10:02,413 INFO  [STDOUT] beforeCompletion method invoked
    12:10:02,413 INFO  [STDOUT] Hibernate: 
        insert 
        into
            Shopping
            (comments, item, id) 
        values
            (?, ?, ?)
    12:10:02,413 INFO  [STDOUT] Hibernate: 
        insert 
        into
            Shopping
            (comments, item, id) 
        values
            (?, ?, ?)
    12:10:02,413 INFO  [STDOUT] Hibernate: 
        insert 
        into
            Shopping
            (comments, item, id) 
        values
            (?, ?, ?)
    12:10:02,413 INFO  [STDOUT] afterCompletion method invoked
    12:10:02,413 INFO  [STDOUT] ********** ECommerceTest doShopping FIN ************



    ■  Doomed Transactions


    We have seen two ways in which a container-managed transaction can be rolled back:

    One way is when the EJB container throws a RemoteException or RunTimeException for system exceptions such as database or network connection errors. When a system exception occurs, the container will automatically rollback the transaction. 
    Como del lado cliente no se espera manejar excepciones de sistema la aplicación simplemente deberá catchear la excepcion de systema y relanzar una EJBException.

    The second way a container-managed transaction can be rolled back is to throw an application exception with the annotation @ApplicationException(rollback=true). An application exception is an exception that occurs while executing business logic, typically some kind of validation. The client application is expected to handle an application exception. We may optionally decide to rollback an application exception, using the rollback=true qualifier, depending on the application's business logic.

    The third way a container-managed transaction can be rolled back is to use the SessionContext interface setRollbackOnly() method. The transaction is not rolled back immediately but on completion of the method. 
    We call this dooming a transaction.

    Ejemplo:

      ....
      ....
     @PersistenceContext(unitName="examplePersistenceUnit")
    private EntityManager em;
    private Shopping sc;
    private List<String> items;

    public void addItem(String item) {
         // con esto indicamos que la transaccion se transforma en una doomed transaction,
         ctx.setRollbackOnly();

    }

    ■  Concurrencia y bloqueos de base de datos (database locking)


    A. Problemas de las transacciones concurrentes y los niveles de aislamiento


    El aislamiento es uno de los 4 atributos (ACID) de toda transaccion. 
    Este atributo cobra fuerza ante los problemas de concurrencia que surgen cuando dos o mas transacciones operan sobre los mismos datos.

    Existen 3 problemas de aislamiento con las transacciones concurrentes.
    • Dirty read
      • An account entity initially has a balance of 100.
      • Transaction 1 updates the balance to 200.
      • Transaction 2 reads new balance of 200.
      • Transaction 1 rolls back restoring the balance to the original value of 100.
      • Transaction 2 now has an incorrect, or dirty, value of 200.
    • Unrepeatable read
      • Transaction 1 reads account id and balance for all accounts with balance less than 150.
      • Transaction 2 updates an account entity changing its balance from 100 to 200.
      • Transaction 1 reads the data again including detailed information such as account name
      • An account which appeared in the initial summary read is not present in the second detailed read.
    • Phantom read
      • Transaction 1 reads account ID and balance for all accounts with balance less than 150.
      • Transaction 2 adds a new account entity with a balance of 100.
      • Transaction 1 reads the data again including detailed information such as account name
      • A new account now appears in the second detailed read which was not present in the initial summary read.
    La Phantom read es similar a Unrepeateble read solo que los datos son insertados en lugar de ser actualizados por la segunda transacción.

    Existen 4 niveles de aislamiento de las transacciones:
    • Read Uncommitted
    • Read Committed
    • Repeatable Read
    • Serializable
    Nivel de aislamientoDirty readUnrepeteable readPhantom read
    SERIALIZABLENo es posibleNo es posibleNo es posible
    REPEATABLE READNo es posibleNo es posibleEs posible
    READ COMMITTEDNo es posibleEs posibleEs posible
    READ UNCOMMITTEDEs posibleEs posibleEs posible
    Estos niveles de aislamiento son parte del estandar SQL92 para base de datos relacionales (RDBMS).

    El nivel mas estricto de aislamiento (serializable) ataca con los 3 primeros problemas (dirty read, Unrepeteable read y Phantom read), pero trae aparejado grandes problemas de performance.mientras que un nivel menos estricto, son los que se usan en la practica.
    La mayoría de las BD no proveen el nivel de aislamiento Read Uncommitted.

    El nivel por defecto es Read Committed o Repeatable Read. En el caso de Oracle, por ejemplo, trae por defecto el nivel Read Committed

    El API de EJB3 no permite setear el nivel de aislamiento. La especificación asume que un EJB3 Container tiene un nivel de asilamiento de Read Committed


    B.  Pessimistic locking & Optimistic locking (@Version)


    Un clásico problema es que dos transacciones actualicen el mismo dato (registro en BD).
    Ejemplo:
    • Transaction 1 reads the Customer entity
    • Transaction 2 reads the same Customer entity
    • Transaction 1 updates its copy of firstName to MICHAEL
    • Transaction 2 updates its copy of lastName to ANGELO
    • Transaction 2 commits data - value on database is LEONARDO ANGELO
    • Transaction 1 commits data - value on database is MICHAEL DAVINCI
    Como podemos ver, la actualizacion de la transaccion 2 se pierde o pisa por la actualizacion de la transacción 1 quedando en el registro el valor de MICHAEL DAVINCI y perdiendo el valor de LEONARDO ANGELO

    Para solucionar esto existen dos estrategias que se pueden tomar:

    • Bloqueo Pesimista: En este caso, se bloquea el registro para su uso exclusivo hasta que haya terminado con el. Tiene mucho mejor integridad que el bloqueo optimista, pero es necesario tener cuidado con el diseño de su aplicación para evitar los deadlocks (situación en la que dos o mas acciones quedan esperando a que termine de ejecutarse otra acción).
    Para el ejemplo anterior, la transacción 1 bloqueara el Customer entity y solo cuando termine su ejecucion recién la transacción 2 podrá ejecutar su commit. Con lo cual el valor que va a quedar en ese registro sera el de LEONARDO ANGELO.

    Una implementacion de este bloqueo es mediante la sintaxis SELECT FOR UPDATE

    • Bloqueo Optimista: Es una estrategia que lee un registro, toma nota de un número de versión y comprueba que la versión no ha cambiado antes de escribir el registro nuevo. Al escribir el registro comprueba que no se haya actualizado entre cuando se compruebe la versión y escribe el registro en el disco.Si  la versión es diferente a la suya anula la transacción.Esta estrategia es más aplicable a sistemas de gran volumen y arquitecturas de tres niveles.

    El bloqueo optimista es implementado por el API de EJB3 mediante el campo Version y la anotación @Version

    Ejemplo:

    @Entity
    public class Customer implements java.io.Serializable {

    private int id;
    private String firstName;
    private String lastName;
    private int version;
    ...


    @Version
    protected int getVersion() { 
    return version; 
    }

    protected void setVersion(int version) {
    this.version = version;
    }

    ...
    }

    En este ejemplo, elegimos que sea un int, pero también podria ser Integer, Short, short, Long, long y Timestamp


    ■  BMT - Transacciones demarcadas por el Bean

    El comportamiento por default de un EJB Container es utilizar el CMT como manejador de transacciones. Sin embargo es posible decirle a un EJB que sea el quien se encargue de demarcar las transacciones (es decir, cuando empieza y cuando termina una transacción). Para esto, se utilizara BMT. Para tal propósito se utiliza JTA. Específicamente, JTA es implementado por la interface javax.transaction.UserTransaction

    Ejemplo:

    public class AddCustomerServlet extends HttpServlet {
    @PersistenceUnit
    private EntityManagerFactory emf;
    @Resource
    private UserTransaction utx;


    try{

    EntityManager em = emf.createEntityManager();
    ...
    ...
    utx.commit();

    } catch (Exception e) {
    try {
    utx.rollback();
    } catch (Exception ee) {}
    throw new ServletException(e);
    } finally {
    em.close();
    }
    }
    }








    Descargas de ejemplos de transacciones EJB3-JPA-Hibernate

    Por favor, haga clic aqui para descargar los ejemplos de este tutorial.


    Bibliografia



    * EJB 3 Developer Guide - A Practical Guide for developers and architects to the Enterprise Java Beans
       Standard - Michael Sikora - 2008 [1]