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 .
Enterprise JavaBeans defines the following values for the TransactionAttribute
metadata annotation:
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
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 theEJBContext.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.
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:
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.