|
This Tech Tip reprinted with permission by java.sun.com
A transaction is a series of operations that must be performed
atomically. In other words, each operation in the series must succeed
for the entire transaction to be successful. If any operation in the
transaction does not succeed, the entire transaction fails. At that
point, any operations which have succeeded must be "rolled back" so
that the end state matches what was in place before the transaction
started.
For example, let's say you want to transfer $50 from a savings
account (account number: 12345-1) to a checking account (account number
12345-2). The steps in this transaction can be stated in "pseudo code"
as follows:
BOUNDARY: START TRANSACTION A SUBTRACT 50 DOLLARS FROM SAVINGS ACCOUNT 12345-1 ADD 50 DOLLARS TO THE CHECKING ACCOUNT 12345-2 BOUNDARY: END TRANSACTION A
To run the transaction, the following code is required:
PREPARE (RUN) TRANSACTION A IF TRANSACTION A SUCCEEDED COMMIT TRANSACTION A ELSE ROLLBACK TRANSACTION A
Let's assume that there are sufficient funds to subtract $50
from
the savings account, so the first part of the transaction succeeds.
However, let's also assume that the second part fails. When the
computer attempts to add 50 dollars to the checking account, it
discovers that the checking account is frozen. Because the second part
of the transaction fails, the entire transaction fails. As a result,
the first part of the transaction needs to be rolled back: $50 needs to
be placed back into the savings account 12345-1. This is essential.
Otherwise, each time the computer tries to transfer money from the
savings to the checking account, it will lose the money!
If the entire transaction succeeds, then the entire transaction is
committed, and the results are made permanent.
Note that the commit process takes two phases to complete. In the first
phase, checks are made to see that the transaction ran without error.
If there were no errors, the results are committed in the second phase.
If there were errors in the first-phase check, the transaction is
rolled back. This common transaction strategy is appropriately called
the two-phase commit protocol.
Transactions in the J2EE Environment
There are typically three participants in a transaction: an
application that initiates the request for the transaction, a data
store (such as a database) that the transaction runs against, and an
API (such as a driver) that communicates between the application and
the data store. In the J2EE platform, the API (or driver) is provided
by the J2EE-compliant application server. The application program calls
the application server to perform the transaction.
The Java Transaction API (JTA) is part of the J2EE platform. The API
gives you the ability to perform distributed transactions, that is, an
application can use the API to perform transactions on more than one
data store in the network at the same time. But to do this efficiently,
it helps to have another component operating in the application server:
a J2EE transaction manager. A transaction manager helps to efficiently
schedule and execute the potentially large number of transactions
coming in through the application server.
Many database vendors provide their own transaction managers. However,
a particular DBMS's transaction manager might not work with databases
from different vendors. If you want to work with these heterogeneous
databases, for example, if you want to update multiple databases from
different vendors, you should consider using a JTA transaction with a
corresponding J2EE transaction manager. The JTA specification states
that "A JTA transaction is controlled by the J2EE transaction manager."
Note that the J2EE transaction manager does have one limitation: the
J2EE transaction model is flat. Support for nested transactions is not
part of J2EE. This means that the J2EE transaction manager cannot start
a transaction for an instance until the previous transaction has ended.
A J2EE-conforming application server implements a transaction
manager using the Java Transaction Service (JTS). The JTA provides the
API to call into the lower-level JTS routines. Be sure not to confuse
the JTA with the JTS.
Learning the JTA
There are three separate interfaces that you can use with the
JTA to
perform transactions, and each uses a unique approach to handling
transactions. The interfaces are:
javax.transaction.UserTransaction.
This interface allows you to specify the transaction boundaries,
bypassing a transaction manager.
javax.transaction.TransactionManager.
This interface
allows you to delegate the boundaries of the transactions, as well as
various transaction operations, to the J2EE transaction manager.
javax.transaction.xa.XAResource.
This interface maps
to the X/Open CAE Specification (Distributed Transaction Processing:
The XA Specification) standard for using third-party XA-compliant
transaction managers to perform transactions.
Because most J2EE programmers only use the first interface, the
remainder of this tip focuses on that interface.
EJB Transactions: Container and Bean-Managed
Transactions
In the J2EE environment, the most logical place to perform transactions
is inside of an Enterprise JavaBeans (EJB) technology component (also
called an enterprise bean). There are two approaches you can take if
you use enterprise beans to perform transactions, The first approach
allows the EJB container to manage the transaction boundaries. This
puts less of a burden on the programmer. This approach is called
container-managed transactions. The second approach allows the
programmer more freedom by explicitly defining the boundaries of
transactions in the enterprise bean code. This approach is called
bean-managed transactions.
Container-managed transactions can be used with any type of enterprise
bean (session bean, entity bean, or message-driven bean). With
container-managed transactions, the EJB container sets the boundaries
of the transaction. This is typically done by marking one or more
methods in the bean as individual transactions. The container sets the
transaction boundary just before the beginning of the method, and sets
the end boundary just before the method exits. However, with
container-managed transactions, each method can be only one transaction
-- multiple transactions are not allowed. When deploying a bean, you
specify which of the bean's methods are associated with transactions.
You do this by setting the transaction attributes.
Transaction attributes control the scope of a transaction when
one
enterprise bean method calls another enterprise bean method. The JTA
specification states that an enterprise bean method can be marked with
one of six different transaction attributes in the EJB deployment
descriptor. The transaction attribute indicates how the EJB container
should treat the method called by the client enterprise bean when
transactions are involved.
Transaction attributes appear in the EJB deployment descriptor as
follows:
<ejb-jar> ... <enterprise-beans> ... </enterprise-beans> <assembly-descriptor> <container-transaction> <method> <ejb-name>BankBean</ejb-name> <method-intf>Remote</method-intf> <method-name>transferMoney</method-name> <method-params> <method-param>java.lang.String</method-param> <method-param>java.lang.String</method-param> <method-param>java.lang.double</method-param> </method-params> </method> <trans-attribute>Required</trans-attribute> </container-transaction> </assembly-descriptor> </ejb-jar>
Here is what the specification says about each of the six transaction
attributes:
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. Most container-managed transactions use Required.
RequiresNew - If the client is
running within a
transaction and invokes the enterprise bean's method, the container
suspends the client's transaction, starts a new transaction, delegates
the call to the method, and finally 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.
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.
There are two ways to roll back a container-managed transaction. If a
system exception is thrown, the container automatically rolls back the
transaction. You can also roll back a transaction by invoking the setRollbackOnly()
method of the EJBContext
interface. This instructs the container to roll back the transaction.
If an enterprise bean throws an application exception, the rollback is
not automatic, but the rollback can be initiated by a call to setRollbackOnly().
Note that you cannot invoke some JTA methods while using
container-managed transactions. That's because these methods are
reserved for use with bean-managed transactions. These methods are:
- Any resource-specific functions that conflict with the
transactional semantics, such as the
commit(),
setAutoCommit(), and rollback()
methods of java.sql.Connection
- The
getUserTransaction() method
of javax.ejb.EJBContext
- Any method of
javax.transaction.UserTransaction
In a bean-managed transaction, the code in the session or
message-driven bean explicitly marks the boundaries of the transaction.
An entity bean cannot have bean-managed transactions, it must use
container-managed transactions. When you code a bean-managed
transaction for session or message-driven beans, you typically can use
either JDBC or JTA transactions. A JDBC transaction is controlled by
the transaction manager of the database management system, and not by
the J2EE transaction manager. To perform a JDBC transaction, use the commit()
and rollback() methods of the java.sql.Connection
interface. The beginning of a transaction is generally assumed with the
first SQL statement that follows the most recent commit(),
rollback(), or connect()
statement. For JTA transactions, you can invoke the begin(),
commit(), and rollback()
methods of the javax.transaction.UserTransaction
interface. The begin() and commit()
methods mark the transaction boundaries. If the transaction operations
fail, an exception handler typically invokes the rollback()
method, and throws an EJBException. The
following code shows how to use the JTA javax.transaction.UserTransaction
interface to perform a bean-managed transaction:
UserTransaction ut = context.getUserTransaction();
try { ut.begin(); // Do whatever transaction functionality is necessary ut.commit(); } catch (Exception ex) { try { ut.rollback(); } catch (SystemException syex) { throw new EJBException ("Rollback failed: " + syex.getMessage()); } throw new EJBException ("Transaction failed: " + ex.getMessage()); }
The code to perform a JDBC bean-managed transaction is similar. Note,
however, that the code turns off the auto-commit on the database
connection. This way, the database treats all subsequent operations as
a single transaction (until the code calls the commit()
method) .
try { Connection con = makeDatabaseConnection(); con.setAutoCommit(false); // Do whatever database transaction functionality // is necessary con.commit(); } catch (Exception ex) { try { con.rollback(); } catch (SQLException sqx) { throw new EJBException("Rollback failed: " + sqx.getMessage()); } } finally { releaseDatabaseConnection(); }
Here are a few rules stated by the JTA specification:
In a stateless session bean with bean-managed
transactions, a business method must commit or roll back a transaction
before returning. However, a stateful session bean does not have this
restriction. In a stateful session bean with a JTA transaction, the
association between the bean instance and the transaction is retained
across multiple client calls. Even if each business method called by
the client opens and closes the database connection, the association is
retained until the instance completes the transaction. In a stateful
session bean with a JDBC transaction, the JDBC connection retains the
association between the bean instance and the transaction across
multiple calls. If the connection is closed, the association is not
retained.
There is one method limitation with JTA bean-managed transactions: do
not invoke the getRollbackOnly() and setRollbackOnly()
methods of the EJBContext interface (these
methods should be used only in container-managed transactions). For
bean-managed transactions, invoke the getStatus()
and rollback() methods of the UserTransaction
interface instead. And, be sure not to invoke any resource-specific
functions that conflict with the transactional semantics.
For more information about JTA and JTS, see the J2EE
Transaction page.
Running the Sample Code for the Java Transaction API
Tip
- Download
the sample archive for the Java Transaction API tip.
- Change to the directory where you downloaded the sample
archive. Uncompress the JAR file for the sample archive as follows:
jar xvf ttJan2005jta.jar
The result is a directory called jta with
source code, compiled classes, and other supporting files.
- Start the application server. The J2EE 1.4 SDK contains the
Sun
Java System Application Server Platform Edition 8. Note that to work
properly, the sample code requires Sun Java Application Server 8.1
4Q2004 or later.
- Start the PointBase database server
- Change to the
jta directory. Edit
the Ant script (build.xml)
to point to your J2EE home directory.
- Enter the command:
ant create-db
on the command line. This creates a database and fills it with an
"account" table. If the database stalls when trying to dump the table,
shut down and restart PointBase.
- Enter the command:
ant build
This creates a directory called ejb and fills
it with two JAR files: bank-client.jar and bank-ejb.jar.
Deploy bank-ejb.jar. You can do
this using the Sun
Java System Application Server Platform Edition 8 Admin Console, (under
Applications->EJB Modules), or by copying the file into the
Autodeploy directory link (if you installed the server with that
option).
- Change to the
ejb directory, and
enter the following command:
appclient -client bank-client.jar
In response, you should see the following:
--OUTPUT FROM APP CLIENT BEGIN-- Using Bean Transaction Management (Database Transaction Manager)... Balance of 12345-01 is: 100.0 Balance of 12345-02 is: 0.0 Now transferring 23.43 from 12345-01 to 12345-02 Balance of 12345-01 is: 76.57 Balance of 12345-02 is: 23.43 Now transferring 23.43 from 12345-01 to fictional account number 12345-10 Balance should be the same as before... Balance of 12345-01 is: 76.57 Balance of 12345-02 is: 23.43 Now Using JTA Container Transaction Management... Now transferring 23.43 from 12345-01 to fictional account number 12345-10 Exception was caught. Balance should again be the same as before... Balance of 12345-01 is: 76.57 Balance of 12345-02 is: 23.43 --OUTPUT FROM APP CLIENT END--
Copyright (c) 2004-2005 Sun Microsystems, Inc.
All Rights Reserved.
Related Tips
|
You can share your information about this topic using the form below!
Please do not post your questions with this form! Thanks.