Transactions

EJB Transaction Management Example

1. Introduction

Transactions free the application programmer from dealing with the complex issues of failure recovery and multi-user programming.

The transactional system ensures that a unit of work either fully completes, or the work is fully rolled back.

2. Transaction Management Type in EJB

The Enterprise Bean Provider and the client application programmer are not exposed to the complexity of distributed transactions.

The Bean Provider can choose between using programmatic transaction demarcation in the enterprise bean code (this style is called bean-managed transaction demarcation)  or  declarative transaction demarcation performed automatically by the EJB container (this style is called container-managed transaction demarcation).

By default, a session bean or message-driven bean has container managed transaction demarcation if the transaction management type is not specified. The Bean Provider of a session bean or a message-driven bean can use the TransactionManagement annotation to declare  transaction type . The value of the TransactionManagement annotation is either CONTAINER or BEAN.

3. Container Managed Transaction ( CMT )

With container-managed transaction demarcation, the container demarcates transactions per instructions provided by the developer in metadata annotations or in the deployment descriptor.

With CMT, transactions are started and completed (with either a commit or rollback) by the container .

CMT Bean
CMT Bean

Enterprise JavaBeans defines the following values for the TransactionAttribute metadata annotation:

Transaction Attributes and Scope
Transaction Attributes and Scope

A T1 transaction is associated with the client that calls a method in the enterprise bean and T2 transaction is started by the container just before the method executes. The word “None” means that the business method does not execute within a transaction controlled by the container.

3.1 Setting Transaction Attributes

Transaction attributes are specified by decorating the enterprise bean class or method with a javax.ejb.TransactionAttribute annotation and setting it to one of the javax.ejb.TransactionAttributeType constants.
By default, if a TransactionAttribute annotation is not specified for a method of an enterprise bean with container-managed transaction demarcation, the value of the transaction attribute for the method is defined to be REQUIRED.

If you decorate the enterprise bean class with @TransactionAttribute, the specified TransactionAttributeType is applied to all the business methods in the class. Decorating a business method with @TransactionAttribute applies the TransactionAttributeType only to that method. If a @TransactionAttribute annotation decorates both the class and the method, the method TransactionAttributeType overrides the class TransactionAttributeType.

The following code snippet demonstrates how to use the @TransactionAttribute annotation:

package com.javacodegeeks.example.beans;

import javax.ejb.Stateless;
import javax.ejb.TransactionAttribute;
import javax.ejb.TransactionAttributeType;

@Stateless
@TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
public class SampleBean {
    ...
    @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
    public void firstMethod() {...}

    @TransactionAttribute(TransactionAttributeType.MANDATORY)
    public void secondMethod() {...}

    public void thirdMethod() {...}

}

In this example, the SampleBean class’s transaction attribute has been set to NotSupported, firstMethod has been set to RequiresNew, and secondMethod has been set to Mandatory . Because a @TransactionAttribute set on a method overrides the class @TransactionAttribute, calls to firstMethod will create a new transaction, and calls to secondMethod must use the transaction of the client. Calls to thirdMethod do not take place within a transaction.

3.2 Container-Managed Transaction Demarcation

Transaction Context Scope
Transaction Context Scope

3.3 Rolling Back a Container-Managed Transaction

There are two ways to roll back a container-managed transaction. First, if a system exception is thrown, the container will automatically roll back the transaction. Second, by invoking the setRollbackOnly method of the EJBContext interface, the bean method instructs the container to roll back the transaction. If the bean throws an application exception, the rollback is not automatic but can be initiated by a call to setRollbackOnly.

3.4 Sample Scenario for Transaction Attribute to be In Action

Required New – Taking an example of a payment method to a 3rd party account from a banking module that deducts the amount of bank account when a transfer is successful (or nothing is performed is it fails), regardless if this transfer is successful or not (with a rollback), the logging facility would still need to be functional to log the status of the transaction and it must not be affect by the rollback of the transfer.

Mandatory – If process is too lengthy to be contained in one method and need to split the codes into two or more sub-process methods, having the other sub-process methods to be annotated with MANDATORY is a good choice.

Never – A method should be annotated with TransactionAttributeType.NEVER if it only consist of logics that “NEVER” touches the database or any invocation of other methods which are transactional.

Not Supported – Better suited for methods that query objects which carries static data that won’t be expected to be changed or to be transactionally involved with other business transaction. It can be querying methods for statically permanent data like country list, region list, gender list, etc. Methods that query data to especially establish drop-down list options in the selection box of web-forms are very well suited to be annotated with NOT_SUPPORTED. Annotating NOT_SUPPORTED in methods like these will greatly save applications from transaction overhead(s) .

3.5 Session Synchronization ( Stateful Session bean transaction )

In the case of a stateful session bean, it is possible that the business method or interceptor method that started a transaction completes without committing or rolling back the transaction. In such a case, the container must retain the association between the transaction and the instance across multiple client calls until the instance commits or rolls back the transaction. When the client invokes the next business method, the container must invoke the business method in this transaction context.

If a session bean class implements the javax.ejb.SessionSynchronization interface or uses the session synchronization annotations, the container must invoke the afterBegin, beforeCompletion, and afterCompletion callbacks on the instance as part of the transaction commit protocol.

  • The container invokes the afterBegin method on an instance before it invokes the first business method in a transaction.
  • The container invokes the beforeCompletion method to give the enterprise bean instance the last chance to cause the transaction to rollback. The instance may cause the transaction to roll back by invoking the EJBContext.setRollbackOnly method.
  • The container invokes the afterCompletion(boolean committed) method after the completion of the transaction commit protocol to notify the enterprise bean instance of the transaction outcome.

CartBean.java

package com.javacodegeeks.example.beans;

import java.util.ArrayList;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.ejb.AfterBegin;
import javax.ejb.AfterCompletion;
import javax.ejb.BeforeCompletion;
import javax.ejb.Remove;
import javax.ejb.Stateful;
import javax.ejb.TransactionAttribute;
import javax.ejb.TransactionAttributeType;
import javax.ejb.TransactionManagement;
import javax.ejb.TransactionManagementType;

/**
 *
 * @author jGauravGupta
 */

@Stateful
@TransactionManagement(value=TransactionManagementType.CONTAINER)
public class CartBean  {
    private ArrayList items;

    @PostConstruct
    public void init() {
        items = new ArrayList();
        System.out.println("CartBean: init");
    }

    @PreDestroy 
    public void destroy() {
        System.out.println("CartBean: destroy");
    }
    
    @Remove
    public void checkOut() {
        // Release any resources.
        System.out.println("Cart checkout...");
    }

    public void addItem(String item) {
        getItems().add(item);
        System.out.println(item + " item added to cart");
    }

    public void removeItem(String item) {
        getItems().remove(item);
        System.out.println(item + " item removed from cart");
    }

    public ArrayList getItems() {
        return items;
    }
    
    @AfterBegin
    private void afterBegin(){
        System.out.println("A new transaction has started.");
    }
    
    @BeforeCompletion
    private void beforeCompletion(){
        System.out.println("A transaction is about to be committed.");
    }
    
    @AfterCompletion
    private void afterCompletion(boolean committed) {
        System.out.println("a transaction commit protocol has completed, and tells the instance whether the transaction has been committed or rolled back , based on committed value : " + committed);
    }
    
}

If the client request is not associated with a transaction

NO_TX_Client_Tester.java

package com.javacodegeeks.example.tester.non_tx;

import com.javacodegeeks.example.beans.CartBean;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 *
 * @author jGauravGupta
 */
@WebServlet(name = "NO_TX_Client_Tester", urlPatterns = {"/NO_TX_Client_Tester"})
public class NO_TX_Client_Tester extends HttpServlet {

    protected void processRequest(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        try (PrintWriter out = response.getWriter()) {

            CartBean cartBean = lookupCartBeanBean();

            cartBean.addItem("Smart Watch");
            cartBean.addItem("iPhone");
            cartBean.addItem("Shoes");

            out.println("Cart Item Size : " + cartBean.getItems().size());

            cartBean.checkOut();
        }
    }


    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        processRequest(request, response);
    }

    private CartBean lookupCartBeanBean() {
        try {
            Context c = new InitialContext();
            return (CartBean) c.lookup("java:global/CMT_Example/CartBean!com.javacodegeeks.example.beans.CartBean");
        } catch (NamingException ne) {
            Logger.getLogger(getClass().getName()).log(Level.SEVERE, "exception caught", ne);
            throw new RuntimeException(ne);
        }
    }

}
Output

Verify the following output in NetBeans console :

Info:   A new transaction has started.
Info:   Smart Watch item added to cart
Info:   A transaction is about to be committed.
Info:   a transaction commit protocol has completed, and tells the instance whether the transaction has been committed or rolled back , based on committed value : true
Info:   A new transaction has started.
Info:   iPhone item added to cart
Info:   A transaction is about to be committed.
Info:   a transaction commit protocol has completed, and tells the instance whether the transaction has been committed or rolled back , based on committed value : true
Info:   A new transaction has started.
Info:   Shoes item added to cart
Info:   A transaction is about to be committed.
Info:   a transaction commit protocol has completed, and tells the instance whether the transaction has been committed or rolled back , based on committed value : true
Info:   A new transaction has started.
Info:   Cart checkout...

If the client request is associated with a transaction

TX_Client_Tester.java

package com.javacodegeeks.example.tester.tx;

import com.javacodegeeks.example.beans.CartBean;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.annotation.Resource;
import javax.ejb.Singleton;
import javax.ejb.TransactionManagement;
import javax.ejb.TransactionManagementType;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.transaction.HeuristicMixedException;
import javax.transaction.HeuristicRollbackException;
import javax.transaction.NotSupportedException;
import javax.transaction.RollbackException;
import javax.transaction.SystemException;
import javax.transaction.UserTransaction;

/**
 *
 * @author jGauravGupta
 */
@Singleton
@TransactionManagement(TransactionManagementType.BEAN)
public class CartProcess {

    @Resource
    private UserTransaction ut;

    public void executeCartProcess() {
        try {
            Context c = new InitialContext();
            CartBean cartBean = (CartBean) c.lookup("java:global/CMT_Example/CartBean!com.javacodegeeks.example.beans.CartBean");

            ut.begin();
            cartBean.addItem("Smart Watch");
            cartBean.addItem("iPhone");
            cartBean.addItem("Shoes");

            System.out.println("Cart Item Size : " + cartBean.getItems().size());
            ut.commit();

            cartBean.checkOut();

        } catch (NamingException ex) {
            Logger.getLogger(CartProcess.class.getName()).log(Level.SEVERE, null, ex);
        } catch (RollbackException | HeuristicMixedException | HeuristicRollbackException | SecurityException | IllegalStateException | SystemException | NotSupportedException ex) {
            try {
                ut.rollback();
                Logger.getLogger(CartProcess.class.getName()).log(Level.SEVERE, null, ex);
            } catch (IllegalStateException | SecurityException | SystemException ex1) {
                Logger.getLogger(CartProcess.class.getName()).log(Level.SEVERE, null, ex1);
            }
        }
    }

}

Output

Verify the following output in NetBeans console :

Info:   CartBean: init
Info:   A new transaction has started.
Info:   Smart Watch item added to cart
Info:   iPhone item added to cart
Info:   Shoes item added to cart
Info:   Cart Item Size : 3
Info:   A transaction is about to be committed.
Info:   a transaction commit protocol has completed, and tells the instance whether the transaction has been committed or rolled back , based on committed value : true
Info:   A new transaction has started.
Info:   Cart checkout...
Info:   CartBean: destroy

4. Bean Managed Transaction ( BMT )

While its true that the ejb container is usually pretty smart about handling transactions, its also not as smart as a real human being and probably isn’t able to handle complex database transactions and rollbacks. This is where bean managed transactions come in. By handling your own transactions you can avoid some major pitfalls.

With bean-managed transaction demarcation, the enterprise bean code demarcates transactions using the javax.transaction.UserTransaction interface. All resource manager accesses between the UserTransaction.begin and UserTransaction.commit calls are part of a transaction.

While an instance is in a transaction, the instance must not attempt to use the resource-manager specific transaction demarcation API (e.g. it must not invoke the commit or rollback method on the java.sql.Connection interface or on the javax.jms.Session interface).

package com.javacodegeeks.example.beans;

import javax.annotation.Resource;
import javax.ejb.Stateless;
import javax.ejb.TransactionManagement;
import javax.ejb.TransactionManagementType;
import javax.transaction.UserTransaction;

@Stateless
@TransactionManagement(value=TransactionManagementType.BEAN)
public class AccountBean {
   
   @Resource
   private UserTransaction userTransaction;

   public void withdrawAmount(long accountId , double fund) {

      try{
         userTransaction.begin();
         
         // TO DO withdrawAmount ....
        
         userTransaction.commit();
      } catch (InsufficientFundException exception){
         userTransaction.rollback();
      }
   }

}

In this example, we made use of UserTransaction interface to mark beginning of transaction using userTransaction.begin() method call. We mark completion of transaction by using userTransaction.commit() method and if any exception occured during transaction then we rollback the complete transaction using userTransaction.rollback() method call.

4.1 BMT Suspend the client transaction

If a transaction is in progress when a method on a BMT bean is called, the transaction is suspended. Temporarily.The transaction just sits there waiting for the BMT bean to complete its work. Work that’s not part of the caller’s original transaction. Once the BMT method finishes and is popped off the stack, the original transaction kicks back in, right where it left off.

BMT bean
BMT bean

The things that happen while the transaction is suspended won’t be rolled back if the suspended transaction (after it comes back to life) fails to commit.

5. setRollbackOnly() lives in TWO interfaces

CMT beans can use only the EJBContext.setRollbackOnly() and  BMT beans can use only the UserTransaction.setRollbackOnly().

The CMT bean knows about the transaction’s status using EJBContext.getRollbackOnly() method  , If transaction marked for rollback then getRollbackOnly() method  returns true and otherwise returns false.

The BMT bean knows about the transaction’s status using UserTransaction.getStatus() method , The getStatus() method returns an int representing a constant for things like: STATUS_ACTIVE, STATUS_COMMITTED, STATUS_COMMITTING, STATUS_MARKED_ROLLBACK and STATUS_ROLLING_BACK etc.

6. Transactions Boundaries

6.1 JMS API

  • The Bean Provider should not make use of the JMS request/reply paradigm (sending of a JMS message, followed by the synchronous receipt of a reply to that message) within a single transaction. Because a JMS message is typically not delivered to its final destination until the transaction commits, the receipt of the reply within the same transaction will not take place.
  • A transaction starts before the dequeuing of the JMS message and, hence, before the invocation of the message-driven bean’s onMessage method. If the onMessage method does not successfully complete or the transaction is rolled back, message redelivery semantics apply.

6.2 Asynchronous Method

The client’s transaction context does not propagate with an asynchronous method invocation. The semantics of the REQUIRED transaction attribute for an asynchronous method are the same as REQUIRES_NEW.

6.3 Timing of Return Value Marshalling

When demarcating a container-managed transaction for a business method invocation through a remote view or web service view, the container must complete the commit protocol before marshalling the return value.

7. Download the NetBeans Project

Download the NetBeans project for this tutorial:

Download
You can download the full source code of this example here: Transaction_Management_Example.zip

8. Conclusion

BMT bean runs only in the transactions the bean itself creates and starts so that it defeats the whole point of a component model usability. With BMT, you can reduce the scope of a transaction but using CMT, you cannot mark a transaction at anything smaller than a single method.

jGauravGupta

Gaurav is a senior software engineer with a passion for learning. He is an evangelist of netbeans & new technologies and author of JPA Modeler , jBatch Suite etc . He loves to go beyond the same old day to day work and find new and innovative ways to do the same things more effectively.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Inline Feedbacks
View all comments
Back to top button