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.
Usually a transaction consists of more than a single operation. Atomicity requires that all of the operations of a transaction are performed successfully for the transaction to be considered complete. If all of a transaction's operations can't be performed, that none of them are allowed to be performed.
Consistency refers to database consistency. A transaction must move the database from one consistent state to another, and it must preserve the database's semantic and physical integrity.
Isolation requires that each transaction appear to be the only transaction currently manipulating the data in the database. Although other transactions can run concurrently, a transaction shouldn't see these manipulations until and unless they complete successfully and commit their work. Because of interdependencies among updates, a transaction might get an inconsistent view of the data were it to see just a subset of another transaction's updates. Isolation protects a transaction from this sort of data inconsistency.
Isolation is related to transaction concurrency. There are levels of isolation. Higher degrees of isolation limit the extent of concurrency. The highest level of isolation occurs when all transactions can be serialized. That is, the database contents appear as if each transaction ran by itself to completion before the next transaction began. Some applications, however, might be able to tolerate a reduced level of isolation for a higher degree of concurrency. Usually these applications run a greater number of concurrent transactions, even it transactions are reading data that might be partially updated and possibly inconsistent.
Durability means that updates made by committed transactions persist in the database regardless of failure conditions. Durability guarantees that committed updates remain in the database despite failures that occur after the commit operation, and that the databases can be recovered after a system or media failure.
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.
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.
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.
Guarantees that the work performed by the associated method is within a global transaction context. If the caller already has a transaction context, the container uses the same context. If the caller doesn't have a transaction context, the container begins a new transaction automatically. Using this attribute makes it easy to compose multiple beans and coordinate the work of all the beans using the same global transaction.
Used when you don't want the method associated with an existing transaction. It ensures that the container always begins a new transaction.
Permits the method to avoid using a global transaction. Use this attribute only when a bean's method accesses just one transaction resource or no transaction resources, and the method doesn't invoke another enterprise bean. Because this attribute avoids the cost associated with global transactions, using it optimizes your bean. If this attribute is set and a global transaction already exists, the container invokes the method and it joins the existing global transaction. If there is no global transaction, the container starts a local transaction for the method and the transaction completes at the end of the method.
Also permits the bean to avoid using a global transaction. If a client calls the method with a transaction context, the container suspends it. At the end of the method, the global transaction resumes.
The client that calls a method with this transaction attribute must already have an associated transaction. If it doesn't, the container throws a javax.transaction.TransactionRequiredException
. Using this attribute makes the bean less flexible for composition because it makes assumptions about the caller's transaction.
The client that calls a method with this transaction attribute must not have a transaction context. If it does, the container throws an exception.
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:
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(); } ...
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.
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.
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.
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.
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.
When a client receives a checked exception for a transaction not marked for rollback, its safest course is to roll back the transaction. The client does this by either marking the transaction as rollback only or, if the client has actually started the transaction, calling the rollback()
method to actually roll back the transaction.
The client can also throw its own checked exception or re-throw the original exception. By throwing an exception, the client lets other programs further up the transaction chain decide whether to abort the transaction. Usually, however, it's preferable for the code or program closest to the occurrence of the problem to make the decision about continuing the transaction or not.
The client can continue with the transaction. The client can evaluate the exception message and decide if calling the method again with difference parameters is likely to succeed. You must remember, however, that retrying a transaction is potentially dangerous. Your code doesn't know if the enterprise bean properly cleaned up its state.
Clients that are calling stateless session beans, however, can retry the transaction if they can determine the problem from the thrown exception. Because the called bean is stateless, there is no improper state to worry about.
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.