Java Technology Home Page
A-Z Index

Java Developer Connection(SM)
Online Training

Downloads, APIs, Documentation
Java Developer Connection
Tutorials, Tech Articles, Training
Online Support
Community Discussion
News & Events from Everywhere
Products from Everywhere
How Java Technology is Used Worldwide
 
Training Index

Writing Advanced Applications
Chapter 3 Continued: Managing Transactions

[<<BACK] [CONTENTS] [NEXT>>]

Wouldn't it be great if every operation your application attempts succeeds? Unfortunately, in the multi-threaded world of distributed applications and shared resources, this is not always possible.

Why? First of all, shared resources must maintain a consistent view of the data to all users. This means reads and writes have to be managed so users do not overwrite each other's changes, or transaction errors do not corrupt data integrity. Also, if you factor in intermittent network delays or dropped connections, the potential for operations to fail in a web-based application increases as the number of users increases.

If operation failures are unavoidable, the next best thing is to recover safely, and that is where transaction management fits in. Modern databases and transaction managers let you undo and restore the state of a failed sequence of operations to ensure the data is consistent for access by multiple threads.

This section adds code to SellerBean from the auction house example so it can manage its auction item insertion transaction beyond the default transaction management provided by its container.


Why Manage Transactions?

When you access a databases using the JDBCTM application programming interface (API), all operations are run with an explicit auto commit by default. This means any other application viewing this data will see the updated data after each JDBC call.

For simple applications this may be acceptable, but consider the auction application and the sequences that occur when SellerBean inserts an auction item. The user's account is first charged for listing the item, and the item is then added to the list of items up for auction. These operations involve RegistrationBean to debit the account and AuctionItemBean to add the item to the auction list.

In auto commit mode, if the auction item insertion fails, only the listing is backed out, and you have to manually adjust the user's account to refund the listing charge. In the meantime, another thread might try to deduct from the same user's account, find no credit left, and abort when perhaps a few milliseconds later it would have completed.

There are two ways to ensure the debit is backed out if the auction item insertion fails:

  • Add session synchronization code to a container-managed session Bean to gain control over transaction commits and roll backs.

  • Configure JDBC services to transaction commit mode and add code to start, stop, commit, and rollback the transaction. This is a Bean-managed transaction and can be used with an entity or session Bean.

Session Synchronization

A container-managed session Bean can optionally include session synchronization to manage the default auto commit provided by the container. Session synchronization code lets the container notify the Bean when important points in the transaction are reached. Upon receiving the notification, the Bean can take any needed actions before the transaction proceeds to the next point.

Note: A session Bean using Bean-managed transactions does not need session synchronization because it is in full control of the commit.

Container-Managed Example

SellerBean is a session Bean that uses RegistrationBean to check the user ID and password when someone posts an auction item and debit the seller's account for a listing, and AuctionItemBean to add new auction items to the database.

The transacton begins in the insertItem method with the account debit and ends when the entire transaction either commits or rolls back. The entire transaction including the 50 cents debit rolls back if the auction item is null (the insertion failed), or if an exception is caught. If the auction item is not null and the insertion succeeds, the entire transaction including the 50 cents debit commits.

Code

To use session synchronization, a session Bean implements the SessionSynchronzation interface and its three methods, afterBegin, beforeCompletion, and afterCompletion. This example adapts the SellerBean.java code to use session synchronization.
public class SellerBean implements SessionBean, 
			SessionSynchronization {

  private transient SessionContext ctx;
  private transient Properties p = new Properties();
  private transient boolean success = true;

  public void afterBegin() {}

  public void beforeCompletion() {
    if (!success ) {
      ctx.setRollbackOnly();
    }
  }

  public void afterCompletion(boolean state) {}

afterBegin: The container calls this method before the debit to notify the session Bean a new transaction is about to begin. You can implement this method to do any preliminary database work that might be needed for the transaction. In this example, no preliminary database work is needed so this method has no implementation.

beforeCompletion: The container calls this method when it is ready to write the auction item and debit to the database, but before it actually does (commits). You can implement this method to write out any cached database updates or roll back the transaction. In this example, the method calls the setRollbackOnly method on its session context in the event the success variable is set to false during the transaction.

afterCompletion: The container calls this method when the transaction commits. A boolean value of true means the data committed and false means the transaction rolled back. The method uses the boolean value to determine if it needs to reset the Bean's state in the case of a rollback. In this example, there is no need to reset the state in the event of a failure.

Here is the insertItem method with comments showing where the points where the SessionSynchronization methods are called.

public int insertItem(String seller, 
		      String password, 
		      String description, 
		      int auctiondays,
		      double startprice, 
		      String summary) 
		throws RemoteException {

  try{
    Context jndiCtx = new InitialContext(p);

    RegistrationHome rhome = 
	(RegistrationHome) sCtx.lookup("registration");
    RegistrationPK rpk=new RegistrationPK();
    rpk.theuser=seller;
    Registration newseller=rhome.findByPrimaryKey(rpk);

    if((newseller == null) || 
	(!newseller.verifyPassword(password))) {
      return(Auction.INVALID_USER);
    }

//Call to afterBegin
    newseller.adjustAccount(-0.50);

    AuctionItemHome home = (AuctionItemHome) 
	jndiCtx.lookup("auctionitems");
    AuctionItem ai= home.create(seller, 
				description, 
				auctiondays, 
				startprice, 
				summary);
    if(ai == null) {
      success=false;
      return Auction.INVALID_ITEM;
    }
    else {
      return(ai.getId());
    }

    }catch(Exception e){
      System.out.println("insert problem="+e);
      success=false;
      return Auction.INVALID_ITEM;
    }
//Call to beforeCompletion
//Call to afterCompletion

 }

Transaction Commit Mode

If you configure the JDBC services to transaction commit mode, you can have the Bean manage the transaction. To set the JDBC services to commit, call con.setAutoCommit(false) on your JDBC connection. Not all JDBC drivers support commit mode, but to have the Bean control and manage transactions, you need a JDBC driver that does.

Transaction commit mode lets you add code that creates a safety net around a sequence of dependent operations. The JavaTM Transaction API (JTA) provides the hooks you need to create that safety net. But, if you are using the Enterprise JavaBeans architecture, you can do it with a lot less code. You only have to configure the Enterprise JavaBeans server, and specify where the transaction starts, stops, rolls back, and commits in your code.

Server Configuration

Configuring the Enterprise JavaBeans server involves specifying the following settings in a configuration file for each Bean:
  • An isolation level to specify how exclusive a transaction's access to shared data is.

  • A transaction attribute to specify how to handle Bean-managed or container-managed transactions that continue in another Bean.

  • A transaction type to specify whether the transaction is managed by the container or the Bean.

For example, you would specify these settings for the BEA Weblogic server in a DeploymentDescriptor.txt file for each Bean.

Here is the part of the DeploymentDescriptor.txt for SellerBean that specifies the isolation level and transaction attribute. A description of the settings follows.

(controlDescriptors
  (DEFAULT
     isolationLevel            TRANSACTION_SERIALIZABLE
     transactionAttribute      REQUIRED
     runAsMode                 CLIENT_IDENTITY
     runAsIdentity             guest
  ); end DEFAULT
); end controlDescriptors
Here is the equivalent Enterprise JavaBeans 1.1 extended markup language (XML) description that specifies the transaction type. In this example SellerBean is container managed.
<container-transaction> 
    <method> 
        <ejb-name>SellerBean<ejb-name> 
        <method-name>*<method-name> 
    <method> 
    <transaction-type>Container<transaction-type>
    <trans-attribute>Required<trans-attribute> 
<container-transaction>
In this example, SellerBean is Bean managed.
<container-transaction> 
    <method> 
        <ejb-name>SellerBean<ejb-name> 
        <method-name>*<method-name> 
    <method> 
    <transaction-type>Bean<transaction-type>
    <trans-attribute>Required<trans-attribute> 
<container-transaction>
Transaction Attribute Descriptions: An enterprise Bean uses a transaction attribute to specify whether a Bean's transactions are managed by the Bean itself or by the container, and how to handle transactions that started in another Bean.

The Enterprise JavaBeans server can control only one transaction at a time. This model follows the example set by the OMG Object Transaction Service (OTS), and means the current Enterprise JavaBeans specification does not provide a way to nest transactions. A nested transaction is a new transaction that starts from within an existing transaction. While transaction nesting is not allowed, continuing an existing transaction in another Bean is okay.

When a Bean is entered, the server creates a transaction context to manage the transaction. When the transaction is managed by the Bean, you access the context to begin, commit, and rollback the transaction as needed.

Here are the transaction attributes with a brief description for each one. The attribute names changed betweein the 1.0 and 1.1 versions of the Enterprise JavaBeans specification.

1.1 Specification 1.0 Specification


REQUIRED TX_REQUIRED
Container-managed transaction. The server either starts and manages a new transaction on behalf of the user or continues using the transaction that was started by the code that called this Bean.

REQUIRESNEW TX_REQUIRED_NEW
Container-managed transaction. The server starts and manages a new transaction. If an existing transaction starts this transaction, it suspends until this transaction completes.

Specified as Bean transaction-type in deployment descriptor TX_BEAN_MANAGED
Bean-managed transaction. You access the transaction context to begin, commit, or rollback the transaction as needed.

SUPPORTS TX_SUPPORTS
If the code calling this Bean has a transaction running, include this Bean in that transaction.

NEVER TX_NOT_SUPPORTED
If the code calling a method in this Bean has a transaction running, suspend that transaction until the method called in this Bean completes. No transaction context is created for this Bean.

MANDATORY TX_MANDATORY
The transaction attribute for this Bean is set when another Bean calls one of its methods. In this case, this Bean gets the transaction attribute of the calling Bean. If the calling Bean has no transaction attribute, the method called in this Bean throws a TransactionRequired exception.
Isolation Level Descriptions: An enterprise Bean uses an isolation level to negotiate its own interaction with shared data and the interaction of other threads with the same shared data. As the name implies, there are various levels of isolation with TRANSACTION_SERIALIZABLE providing the highest level of data integrity.


Note: Be sure to verify your database can handle the level you choose. In the Enterprise JavaBeans 1.1 specification, only Bean-managed persistence session Beans can set the isolation level.

If the database cannot handle the isolation level, the Enterprise JavaBeans server will get a failure when it tries to call the setTransactionIsolation JDBC method.


TRANSACTION_SERIALIZABLE: This level provides maximum data integrity. The Bean gets what amounts to exclusive access to the data. No other transaction can read or write this data until the serializable transaction completes.

Serializable in this context means process as a serial operation, and should not be confused with serializing objects to preserve and restore their states. Running transactions as a single serial operation is the slowest setting. If performance is an issue, use another isolation level that meets your application requirements, but provides better performance.

TRANSACTION_REPEATABLE_READ: At this level, data read by a transaction can be read, but not modified, by another transaction. The data is guaranteed to have the same value it had when first read, unless the first transaction changes it and writes the changed value back.

TRANSACTION_READ_COMMITTED: At this level, data read by a transaction cannot be read by other transactions until the frist transaction either commits or rolls back.

TRANSACTION_READ_UNCOMMITTED: At this level, data involved in a transaction can be read by other threads before the first transaction either completes or rolls back. The other transactions cannot tell if the data was finally committed or rolled back

Bean-Managed Example

SellerBean is a session Bean that uses RegistrationBean to check the user ID and password when someone posts an auction item and debit the seller's account for a listing, and AuctionItemBean to add new auction items to the database.

The transacton begins in the insertItem method with the account debit and ends when the entire transaction either commits or rolls back. The entire transaction including the 50 cents debit rolls back if the auction item is null (the insertion failed), or if an exception is caught. If the auction item is not null and the insertion succeeds, the entire transaction including the 50 cents debit commits.

For this example, the isolation level is TRANSACTION_SERIALIZABLE, and the transaction attribute is TX_BEAN_MANAGED. The other Beans in the transaction, RegistrationBean and AuctionItemBean, have an isolation level of TRANSACTION_SERIALIZABLE and a transaction attribute of REQUIRED.

Changes to this version of SellerBean over the container-managed version are flagged with comments.

public int insertItem(String seller, 
	String password, 
	String description, 
	int auctiondays, 
	double startprice, 
	String summary) 
	throws RemoteException {

//Declare transaction context variable using the
//javax.transaction.UserTransaction class
  UserTransaction uts= null;

  try{
    Context ectx = new InitialContext(p);

//Get the transaction context
    uts=(UserTransaction)ctx.getUserTransaction();

    RegistrationHome rhome = (
	RegistrationHome)ectx.lookup("registration");
    RegistrationPK rpk=new RegistrationPK();
    rpk.theuser=seller;
    Registration newseller=
	rhome.findByPrimaryKey(rpk);

    if((newseller == null)|| 
	(!newseller.verifyPassword(password))) {
        return(Auction.INVALID_USER);
    }

//Start the transaction
    uts.begin(); 

//Deduct 50 cents from seller's account
    newseller.adjustAccount(-0.50);

    AuctionItemHome home = (
	AuctionItemHome) ectx.lookup("auctionitems");
    AuctionItem ai= home.create(seller, 
	description, 
	auctiondays, 
	startprice, 
	summary);

    if(ai == null) {
//Roll transaction back
        uts.rollback();
        return Auction.INVALID_ITEM;
    }
    else {
//Commit transaction 
        uts.commit();
        return(ai.getId());
    }

    }catch(Exception e){
        System.out.println("insert problem="+e);
//Roll transaction back if insert fails 
        uts.rollback();
        return Auction.INVALID_ITEM;
    }
}

[TOP]


[ This page was updated: 13-Oct-99 ]

Products & APIs | Developer Connection | Docs & Training | Online Support
Community Discussion | Industry News | Solutions Marketplace | Case Studies
Glossary - Applets - Tutorial - Employment - Business & Licensing - Java Store - Java in the Real World
FAQ | Feedback | Map | A-Z Index
For more information on Java technology
and other software from Sun Microsystems, call:
(800) 786-7638
Outside the U.S. and Canada, dial your country's AT&T Direct Access Number first.
Sun Microsystems, Inc.
Copyright © 1995-99 Sun Microsystems, Inc.
All Rights Reserved. Legal Terms. Privacy Policy.