Container-Managed Transactions

In an enterprise bean with container-managed transactions, the EJB container sets the boundaries of the transactions. You can use container-managed transactions with any type of enterprise bean: session, entity, or message-driven. Container-managed transactions simplify development because the enterprise bean code does not explicitly mark the transaction's boundaries. The code does not include statements that begin and end the transaction.

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.

 

Transaction Attributes

A transaction attribute controls the scope of a transaction.

A transaction attribute may have one of the following values:

  • Required
  • RequiresNew
  • Mandatory
  • NotSupported
  • Supports
  • Never

Required

If the client is running within a transaction and invokes the enterprise bean's method, the method executes within the client's transaction. If the client is not associated with a transaction, the container starts a new transaction before running the method.

The Required attribute will work for most transactions. Therefore, you may want to use it as a default, at least in the early phases of development. Because transaction attributes are declarative, you can easily change them at a later time.

RequiresNew

If the client is running within a transaction and invokes the enterprise bean's method, the container takes the following steps:

  1. Suspends the client's transaction
  2. Starts a new transaction
  3. Delegates the call to the method
  4. Resumes the client's transaction after the method completes

If the client is not associated with a transaction, the container starts a new transaction before running the method.

You should use the RequiresNew attribute when you want to ensure that the method always runs within a new transaction.

Mandatory

If the client is running within a transaction and invokes the enterprise bean's method, the method executes within the client's transaction. If the client is not associated with a transaction, the container throws the TransactionRequiredException.

Use the Mandatory attribute if the enterprise bean's method must use the transaction of the client.

NotSupported

If the client is running within a transaction and invokes the enterprise bean's method, the container suspends the client's transaction before invoking the method. After the method has completed, the container resumes the client's transaction.

If the client is not associated with a transaction, the container does not start a new transaction before running the method.

Use the NotSupported attribute for methods that don't need transactions. Because transactions involve overhead, this attribute may improve performance.

Supports

If the client is running within a transaction and invokes the enterprise bean's method, the method executes within the client's transaction. If the client is not associated with a transaction, the container does not start a new transaction before running the method.

Because the transactional behavior of the method may vary, you should use the Supports attribute with caution.

Never

If the client is running within a transaction and invokes the enterprise bean's method, the container throws a RemoteException. If the client is not associated with a transaction, the container does not start a new transaction before running the method.

Summary of Transaction Attributes

Table 14-1 summarizes the effects of the transaction attributes. Both the T1 and T2 transactions are controlled by the container. A T1 transaction is associated with the client that calls a method in the enterprise bean. In most cases, the client is another enterprise bean. A T2 transaction is started by the container just before the method executes.

In the last column of Table 14-1, the word "None" means that the business method does not execute within a transaction controlled by the container. However, the database calls in such a business method might be controlled by the transaction manager of the DBMS.

Setting Transaction Attributes

Because transaction attributes are stored in the deployment descriptor, they can be changed during several phases of J2EE app development: enterprise bean creation, app assembly, and deployment. However, it is the responsibility of an enterprise bean developer to specify the attributes when creating the bean. The attributes should be modified only by an app developer who is assembling components into larger apps. Do not expect the person deploying the J2EE app to specify the transaction attributes.

 

Table 14-1 Transaction Attributes and Scope 
Transaction Attribute
Client's Transaction
Business Method's Transaction
Required None T2
T1 T1
RequiresNew None T2
T1 T2
Mandatory None error
T1 T1
NotSupported None None
T1 None
Supports None None
T1 T1
Never None None
T1 Error

You can specify the transaction attributes for the entire enterprise bean or for individual methods. If you've specified one attribute for a method and another for the bean, the attribute for the method takes precedence. When specifying attributes for individual methods, the requirements differ with the type of bean. Session beans need the attributes defined for business methods, but do not allow them for the create methods. Entity beans require transaction attributes for the business, create, remove, and finder methods. Message-driven beans require transaction attributes (either Required or NotSupported) for the onMessage method.

 

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 app exception, the rollback is not automatic, but may be initiated by a call to setRollbackOnly. For a description of system and app exceptions, see Handling Exceptions.

The source code for the following example is in the j2eetutorial/examples/src/ejb/bank directory. To compile the code, go to the j2eetutorial/examples directory and type ant bank. To create the database tables, type ant create-bank-table. A sample BankApp.ear file is in the j2eetutorial/examples/ears directory.

The transferToSaving method of the BankEJB example illustrates the setRollbackOnly method. If a negative checking balance occurs, transferToSaving invokes setRollBackOnly and throws an app exception (InsufficientBalanceException). The updateChecking and updateSaving methods update database tables. If the updates fail, these methods throw a SQLException and the transferToSaving method throws an EJBException. Because the EJBException is a system exception, it causes the container to automatically roll back the transaction. Here is the code for the transferToSaving method:

public void transferToSaving(double amount) throws InsufficientBalanceException  
{
   checkingBalance -= amount;
   savingBalance += amount;

   try 
   {
      updateChecking(checkingBalance);
      if (checkingBalance < 0.00) 
      {
         context.setRollbackOnly();
         throw new InsufficientBalanceException();
      }
      updateSaving(savingBalance);
   } 
   catch (SQLException ex) 
   {
       throw new EJBException ("Transaction failed due to SQLException: " + ex.getMessage());
   }
}

When the container rolls back a transaction, it always undoes the changes to data made by SQL calls within the transaction. However, only in entity beans will the container undo changes made to instance variables. (It does so by automatically invoking the entity bean's ejbLoad method, which loads the instance variables from the database.) When a rollback occurs, a session bean must explicitly reset any instance variables changed within the transaction. The easiest way to reset a session bean's instance variables is by implementing the SessionSynchronization interface.

 

Synchronizing a Session Bean's Instance Variables

The SessionSynchronization interface, which is optional, allows you to synchronize the instance variables with their corresponding values in the database. The container invokes the SessionSynchronization methods--afterBegin, beforeCompletion, and afterCompletion--at each of the main stages of a transaction.

The afterBegin method informs the instance that a new transaction has begun. The container invokes afterBegin immediately before it invokes the business method. The afterBegin method is a good place to load the instance variables from the database. The BankBean class, for example, loads the checkingBalance and savingBalance variables in the afterBegin method:

public void afterBegin() {

   System.out.println("afterBegin()");
   try {
      checkingBalance = selectChecking();
      savingBalance = selectSaving();
   } catch (SQLException ex) {
       throw new EJBException("afterBegin Exception: " +
           ex.getMessage());
   }
}

The container invokes the beforeCompletion method after the business method has finished, but just before the transaction commits. The beforeCompletion method is the last opportunity for the session bean to roll back the transaction (by calling setRollbackOnly). If it hasn't already updated the database with the values of the instance variables, the session bean may do so in the beforeCompletion method.

The afterCompletion method indicates that the transaction has completed. It has a single boolean parameter, whose value is true if the transaction was committed and false if it was rolled back. If a rollback occurred, the session bean can refresh its instance variables from the database in the afterCompletion method:

public void afterCompletion(boolean committed) {

   System.out.println("afterCompletion: " + committed);
   if (committed == false) {
      try {
         checkingBalance = selectChecking();
         savingBalance = selectSaving();
      } catch (SQLException ex) {
          throw new EJBException("afterCompletion SQLException:
         " + ex.getMessage());
      }
   }
}

 

Methods Not Allowed in Container-Managed Transactions

You should not invoke any method that might interfere with the transaction boundaries set by the container. The list of prohibited methods follows:

  • The commit, setAutoCommit, and rollback methods of java.sql.Connection
  • The getUserTransaction method of javax.ejb.EJBContext
  • Any method of javax.transaction.UserTransaction

You may, however, use these methods to set boundaries in bean-managed transactions.