Managing transactions

You can benefit from developing applications on platforms such as Java 2 Enterprise Edition (J2EE) that support transactions. A transaction-based system simplifies application development because it frees you, the developer, from the complex issues of failure recovery and multi-user programming. Transactions aren't limited to single databases or single sites. Distributed transactions can simultaneously update multiple databases across multiple sites.

A developer usually divides the total work of an application into a series of units. Each unit of work is a separate transaction. As the application progresses, the underlying system ensures that each unit of work, each transaction, fully completes without interference from other processes. If it doesn't, the system rolls back the transaction and completely reverses the work the transaction had performed so that the application is back to the same state before the transaction began.

Characteristics of transactions

Usually transactions refer to operations that access a database. All access to the database occurs in the context of a transaction. All transactions share these characteristics, denoted by the acronym ACID:


Transaction support in the container

An EJB container supports flat transactions, but not nested ones. It also propagates transactions implicitly. This means that you don't have to explicitly pass the transaction context as a parameter, because the container handles this task for the client transparently.

You should keep in mind that JSPs and servlets, while they can act as clients, aren't designed to be transactional components. Use enterprise beans to perform transactional work. When you invoke an enterprise bean to perform the transactional work, the bean and container set up the transaction properly.


Enterprise beans and transactions

Enterprise beans and the EJB container greatly simplify transaction management. Enterprise beans make it possible for an application to update data in multiple databases in a single transaction, and these databases can reside on multiple EJB servers.

Traditionally, an application responsible for managing transactions had to perform these tasks:

Such an application demanded a very skilled developer and it was easy for errors to creep in.

Using enterprise beans, the container manages most if not all aspects of the transaction for you. It starts and ends the transaction and maintains its context throughout the life of the transaction object. Your responsibilities are greatly reduced, especially for transactions in distributed environments.

An enterprise bean's transaction attributes are declared at deployment time. These transaction attributes indicate whether the container manages the bean's transactions, or whether the bean manages its own transactions and to what extent.

Bean- versus container-managed transactions

When an enterprise bean programmatically performs its own transaction demarcation as part of its business methods, that bean is considered to be using bean-managed transaction. (To demarcate a transaction means to indicate where a transaction begins and where it ends.) When a bean defers all transaction demarcation to its EJB container, the container performs the transaction demarcation based on the application assembler's deployment instructions. This is called using container-managed transaction.

Both stateful and stateless session beans can use either type of transaction. A bean can't use both types of transaction management at the same time, however. The bean provider decides which type the session bean will use. Entity beans can use container-managed transactions only.

You might want a bean to manage its own transaction if you want to start a transaction as part of one operation, and then finish the transaction as part of another operation. You might encounter problems, however, if one operation calls the transaction starting method, but no operation calls the transaction ending method.

Whenever possible, you should write enterprise beans that use container-managed transactions. They require less work on your part and are less prone to errors. Also, it's easier to customize a bean with a container-managed transaction and to use it to compose other beans.

Transaction attributes

Enterprise beans that use container-managed transaction have transaction attributes associated with each method of the bean or with the bean as a whole. The attribute value tells the container how it must manage the transactions that involve the bean. There are six different transaction attributes that the application assembler or deployer can associate with each method of a bean:

Local and global transactions

When a single connection to a database exists, the enterprise bean can directly control the transaction by calling commit() or rollback() on the connection. This type of transaction is a local transaction. Using global transactions, all database connections are registered with the global transaction service, which handles the transaction. For a global transaction, the enterprise bean never makes calls directly on a database connection itself.

A bean that uses bean-managed transaction demarcation uses the javax.transaction.UserTransaction interface to identify the boundaries of a global transaction. When a bean uses container-managed demarcation, the container interrupts each client call to control the transaction demarcation, using the transaction attribute set in the bean's deployment descriptor by the application assembler. The transaction attribute also determines whether the transaction is local or global.

For container-managed transactions, the container follows certain rules to determine when it should do a local versus a global transaction. Usually a container calls the method within a local transaction after verifying that no global transaction already exists. It also verifies that it isn't expected to start a new global transaction and that the transaction attributes are set for container-managed transactions. The container automatically wraps a method call within a local transaction if one of the follow conditions is true:


Using the transaction API

All transactions use the Java Transaction API (JTA). When transactions are container managed, the platform handles the demarcation of transaction boundaries and the container uses the JTA API. You never need to use this API in your bean code.

If your bean manages its own transactions, it must use the JTA javax.transaction.UserTransaction interface. This interface allows a client or component to demarcate transaction boundaries. Enterprise beans that use bean-managed transactions use the EJBContext.getUserTransaction() method.

Also, all transactional clients use JNDI to look up the UserTransaction interface. Do this by constructing a JNDI InitialContext using the JNDI naming service, such as shown here:

javax.naming.Context context = new javax.naming.InitialContext();

Once the bean has the InitialContext, it can then use the JNDI lookup() operation to obtain the UserTransaction interface:

javax.transaction.UserTransaction utx = (javax.transaction.UserTransaction)
    context.lookup("java:comp/UserTransaction")

Note than an enterprise bean can obtain a reference to the UserTransaction interface from the EJBContext object. The bean can simply use the EJBContext.getUserTransaction() method rather than having to obtain an InitialContext object and then using the JNDI lookup() method. A transactional client that isn't an enterprise bean, however, must use the JNDI lookup approach.

When the bean or client has the reference to the UserTransaction interface, it can then initiate its own transactions and manage them. That is, you can use the UserTransaction interface methods to begin and commit (or rollback) transactions. You use the begin() method to start the transaction, then the commit() method to commit the changes to the database. Or, you use the rollback() method to abort all changes made within the transaction and restore the database to the state it was in prior to the start of the transaction. Between the begin() and commit() methods, you include the code to carry out the business of the transaction. Here's an example:

public class NewSessionBean implements SessionBean {
    EJBContext ejbContext;
	
    public void doSomething(...) {
        javax.transaction.UserTransaction utx;
        javax.sql.DataSource dataSource1;
        javax.sql.DataSource dataSource2;
        java.sql.Connection firstConnection;
        java.sql.Connection secondConnection;
        java.sql.Statement firstStatement;
        java.sql Statement secondStatement;
		
        java.naming.Context context = new javax.naming.InitialContext();
		
        dataSource1 = (javax.sql.DataSource)
		    context.lookup("java:comp/env/jdbcDatabase1");
        firstConnection = dataSource1.getConnection();
		
        firstStatement = firstConnection.createStatement();
		
        dataSource2 = (javax.sql.DataSource)
		    context.lookup("java:comp/env/jdbcDatabase2");
        secondConnection = dataSource2.getConnection();
		
        secondStatement = secondConnection.createStatement();
		
        utx = ejbContext.getUserTransaction();
		
        utx.begin();
		
        firstStatement.executeQuery(...);
        firstStatement.executeUpdate(...);
        secondStatement.executeQuery(...);
        secondStatement.executeUpdate(...);
		
        utx.commit();
		
        firstStatement.close;
        secondStatement.close
        firstConnection.close();
        secondConnection.close();
    }
    ...
	


Handling transaction exceptions

Enterprise beans can throw application and/or system-level exceptions if they encounter errors while handling transactions. Application-level exceptions arise from errors in the business logic. The calling application must handle them. System-level exceptions, such as runtime errors, transcend the application itself and can be handled by the application, the enterprise bean, or the bean container.

The enterprise bean declares application-level exceptions and system-level exceptions in the throws clauses of its Home and Remote interfaces. You must check for checked exceptions in your client application's try/catch block when calling bean methods.

System-level exceptions

An enterprise bean throws a system-level exception (usually a java.ejb.EJBException, but possibly a java.rmi.RemoteException) to indicate an unexpected system-level failure. For example, it throws an exception if it can't open a database connection. The java.ejb.EJBException is a runtime exception and it isn't required to be listed in the throws clause of the bean's business methods.

System-level exceptions usually require the transaction to be rolled back. Often the container managing the bean does the rollback. Sometimes the client must roll back the transaction, though, especially if transactions are bean-managed.

Application-level exceptions

The bean throws an application-level exception to indicate application-specific error conditions. These are business logic errors, not system problems. Application-level exceptions are exceptions other than java.ejb.EJBException. Application-level exceptions are checked exceptions, which means you must check for them when you call a method that potentially can throw this exception.

The bean's business methods use application exceptions to report abnormal application conditions, such as unacceptable input values or amounts beyond acceptable limits. For example, a bean method that debits an account balance might throw an application exception to report that the account balance isn't sufficient to permit a particular debit operation. A client can often recover from these application-level errors without having to roll back the entire transaction.

The application or calling program gets back the same exception that was thrown, allowing the calling program to know the precise nature of the problem. When an application-level exception occurs, the enterprise bean instance doesn't automatically roll back the client's transaction. The client now has the knowledge and the opportunity to evaluate the error message, take the necessary steps to correct the situation, and recover the transaction. Or the client can abort the transaction.

Handling application exceptions

Because the application-level exceptions report business logic errors, your client must handle these exceptions. While these exceptions might require transaction rollback, they don't automatically mark the transaction for rollback. The client can retry the transaction, although often it must abort and roll back the transaction.

You, as the bean provider, must ensure that the state of the bean is such that if the client continues with the transaction, there is no loss of data integrity. If you can't ensure this, you must mark the transaction for rollback.

Transaction rollback

When your client gets an application exception, first check if the current transaction has been marked for rollback only. For example, a client might receive a javax.transaction.TransactionRolledbackException. This exception indicates that the helper enterprise bean failed and the transaction has been aborted or marked "rollback only". Usually the client doesn't know the transaction context within which the enterprise bean operated. The bean might have operated in its own transaction context separate from the calling program's transaction context, or it might have operated in the calling program's context.

If the enterprise bean operated in the same transaction context as the calling program, then the bean itself (or its container) has already marked the transaction for rollback. When an EJB container marks a transaction for rollback, the client should stop all work on the transaction. Usually a client using declarative transactions gets an appropriate exception, such as javax.transaction.TransactionRolledbackException. Note that declarative transactions are those transactions where the container manages the transaction details.

A client that is itself an enterprise bean should call the javax.ejbEJBContext.getRollbackOnly() method to determine if its own transaction has been marked for rollback.

For bean-managed transactions—those transactions managed explicitly by the client—the client should roll back the transaction by calling the rollback() method from the java.transaction.userTransaction interface.

Options for continuing a transaction

When a transaction isn't marked for rollback, the client has these options:

If you are using the Inprise Application Server, see the "Transaction Management" chapter in the Inprise Application Server's Enterprise JavaBeans Programmer's Guide for additional information about transactions and the Inprise container.