Using Bean Managed Transactions (BMT) in Java EE applications

In a Bean Managed Transaction (BMT), the code in the EJB or Message Driven Bean explicitly marks the boundaries of the transaction. Although beans with container-managed transactions (CMT) require less coding, they have one limitation: When a method is executing, it can be associated with either a single transaction or no transaction at all. If this limitation will make coding your bean difficult, you should consider using bean-managed transactions. How do you code an EJB 3 using Bean Managed Transactions (BMT)

Here is an example:

package com.mastertheboss.ejb;

import javax.ejb.Stateless;
import javax.inject.Inject;
import javax.persistence.PersistenceContext;
import javax.transaction.Status;
import javax.transaction.UserTransaction;
import javax.ejb.TransactionManagement;
import javax.ejb.TransactionManagementType;
import javax.persistence.EntityManager;


@Stateless
@TransactionManagement(TransactionManagementType.BEAN)

public class ManagedComponent {
    /**
     * Ask the container to inject an Entity Manager (EM). As a consequence the EM will be automatically enlisted into any new
     * bean managed transaction
     *
     */
    @PersistenceContext
    private EntityManager entityManager;

    // Inject a UserTransaction for manual transaction demarcation.
    @Inject
    private UserTransaction userTransaction;

    public String executeTransaction() {

        try {
            userTransaction.begin();

            // Execute Bean Managed Transaction here

            userTransaction.commit();

            return result;
        } catch (Exception e) {
            return e.getMessage();
        } finally {
            /*
             * Clean up
             */
            try {
                if (userTransaction.getStatus() == Status.STATUS_ACTIVE)
                    userTransaction.rollback();
            } catch (Throwable e) {
                // ignore
            }
        }
    }
}

As you can see, we specify that an EJB uses Bean Managed Transaction (BMT) by using the @TransactionManagement annotation and setting its TransactionManagementType element to BEAN. Next, the transaction operations are enclosed in a code sequence starting with the UserTransaction ‘s begin method and ending with the commit method. Please note that the above example @Inject the UserTransaction therefore you need to include also CDI dependencies in your project.

On the other hand, if you are not using CDI in your project, you can use the legacy @Resource annotation to inject the UserTransaction in your project:

@Resource
private UserTransaction utx;

A Message Driven Bean with Bean Managed Transaction

Much the same way, we can code a Message Driven Bean using BMT. Here is an example:

import javax.annotation.Resource;
import javax.ejb.ActivationConfigProperty;
import javax.ejb.MessageDriven;
import javax.ejb.MessageDrivenContext;
import javax.ejb.TransactionManagement;
import javax.ejb.TransactionManagementType;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.TextMessage;
import javax.transaction.UserTransaction;

import org.jboss.ejb3.annotation.ResourceAdapter;

@MessageDriven(name = "MDB_BMTExample", activationConfig = { @ActivationConfigProperty(propertyName = "destinationType", propertyValue = "javax.jms.Queue"),
                                                            @ActivationConfigProperty(propertyName = "destination", propertyValue = "queue/testQueue"),
                                                            @ActivationConfigProperty(propertyName = "acknowledgeMode", propertyValue = "Dups-ok-acknowledge") })
@TransactionManagement(value = TransactionManagementType.BEAN)
@ResourceAdapter("hornetq-ra.rar")
public class MDB_BMTExample implements MessageListener
{
   @Resource
   MessageDrivenContext ctx;

   public void onMessage(final Message message)
   {
      try
      {
         
         TextMessage textMessage = (TextMessage)message;
         String text = textMessage.getText();
         System.out.println("message " + text + " received");

         // lets look at the user transaction to make sure there isn't one.
         UserTransaction tx = ctx.getUserTransaction();

         if (tx != null)
         {
            tx.begin();
            System.out.println("we're in the middle of a transaction: " + tx);
            tx.commit();
         }
         else
         {
            System.out.println("something is wrong, I was expecting a transaction");
         }
      }
      catch (Exception e)
      {
         e.printStackTrace();
      }
   }
}

Rolling back an EJB Bean Managed Transaction

In a BMT EJB, either the UserTransaction ‘s rollback() or setRollbackOnly() methods are used to explicitly rollback the transaction. In the above example, we have used the rollback() method of the UserTransaction class, however it is also possible to invoke setRollBackOnly as in this example:

try {
// Starts the transaction
userTransaction.begin;
...
// Commits the transaction
userTransaction.commit();
} catch (FirstException fe ) {
userTransaction.setRollbackOnly();
} catch (SecondException se ) {
userTransaction.setRollbackOnly();
}

So what is the difference between UserTransaction ‘s rollback() or setRollbackOnly() ? The use of the rollback() method results in the immediate rollback of the transaction. On the other hand, using the setRollbackOnly method only marks the transaction for rollback. It will not be rolled back until the transaction actually ends. Delaying the rollback can be advantageous since it permits other activities to be performed, such as logging the error conditions.

Transactions not completed in a BMT

According to the EJB specification a transaction must be finished from a BMT bean if it was opened there.

“If a stateless session bean instance starts a transaction in a business method, it must commit the transaction before the business method returns.
The Container must detect the case in which a transaction was started, but not completed, in the business method, and handle it as follows:

  • Log this as an application error to alert the system administrator.
  • Roll back the started transaction.
  • Discard the instance of the session bean.
  • Throw the java.rmi.RemoteException to the client”

Interview question

Q: And now a nice interview question: what happens if a Bean Managed Transaction EJB calls a CMT EJB in its transaction ? and viceversa ?

A: Actually, it is possible a BMT will start a transaction and then invoke a method of a CMT. As a result, the CMT will not be aware who started the transaction. In contrast, a BMT can only participate in a transaction that it started. It is not possible to propagate a transaction to another BMT EJB.

Found the article helpful? if so please follow us on Socials