Chapter 16. Managing Java Transactions Using JTA

The Java Transaction API

Transactions enable J2EE applications to maintain consistent, durable, and reliable persistent storage of data in a database. In Chapter 13, “Accessing Data Repositories Using JDBC,” an overview for writing applications that use and perform Java Database Connectivity is presented. This chapter builds on the infrastructure of JDBC to create J2EE applications that ensure durable and consistent storage of the application data. The Java technology that provides this capability is the Java Transaction API (JTA). A J2EE application is not useful if the data that it generates cannot be trusted to be consistent and accurate. After all, it is called a relational database because of the relationships between the data. Using database transactions correctly is extremely important so that all data affected by the transaction is modified as a group, or none of the data is modified. Otherwise, the database can become corrupted or contain inconsistent data.

This chapter introduces database transactions using JTA. Specifically, this chapter examines the way transactions are used in a J2EE application that is deployed in WebLogic Server. Managing database transactions using the performance tips provided here will help you to build robust, reliable, high-performance J2EE applications. A Servlet example is also presented to show how these technologies work together in a J2EE application.

Note

▸ For further information on Java Database Connectivity see Chapter 13, p. 403.

Using Transactions to Ensure Data Integrity

Chapter 13 included an example that created a database to maintain inventory for a bookstore. Here, you will extend the bookstore example to add the capability to sell books. This example will provide an introduction to transactions against a single database. A few modifications to the database schema are required to add the price and inventory count to each book. The Books table needs two additional columns, the price as a REAL SQL data type and the number of copies in stock as an INTEGER. The customers will be identified with a name and ID. Each customer will have a balance to be used as the funds for purchasing books. The Customers table will therefore include the customer_name as a VARCHAR, customer_id as an INTEGER, and balance as a FLOAT. Selling books from the inventory requires a new many-to-many relationship between customers and books. Each customer can purchase many books, and each book can be purchased by many customers. The database now requires the Books table and two new tables, the Customers table and an association table called the ShoppingCart table, as shown in Figure 16.1.

The ShoppingCart table allows a many-to-many relationship between Customer and Books.

Figure 16.1. The ShoppingCart table allows a many-to-many relationship between Customer and Books.

The scenario for a customer to purchase a book is as follows:

  1. Customer identifies himself by customer_name.

  2. Customer requests to purchase a book by title.

  3. Verify there is at least one copy of the book.

  4. Verify customer has enough money in his balance to cover the price of the book.

  5. Decrement the copies of the book by one.

  6. Decrement the customer balance by the price of the book.

  7. Insert a new ShoppingCart entry to store the relationship.

Steps 5, 6, and 7 must be performed as a transaction. In other words, a transaction must be established to complete or fail steps 5 through 7 as a group. It is clear that if the transaction only partially completes, the database would be in an invalid state. More than likely, the customer would not be pleased either. This example requires three database updates to be completed together. When all the statements complete successfully, they will be committed to the database. If any operation fails, a rollback will be performed. JDBC throws an SQLException to indicate errors. The rollback is therefore performed in the catch block for SQLException. If no exceptions are thrown, the commit will be executed. By default, JDBC is in auto-commit mode, meaning each statement is automatically committed to the database. You need to disable auto-commit to be able to control transaction management. Assuming a Connection object named con has already been obtained from either the DriverManager or a DataSource, Listing 16.1 shows the try block that performs the sample transaction.

Example 16.1. The Transaction Must Be Rolled Back if an Error Occurs While Updating the Database

try {
    con.setAutoCommit( false );    // disable auto-commit
    Statement s = con.createStatement();
    s.executeUpdate( "UPDATE books SET copies = " + ( copies – 1 ) +
        "WHERE title = " + title );
    s.close();
    s = con.createStatement();
    s.executeUpdate( "UPDATE customers SET balance = " + ( balance – price ) +
        "WHERE customer_name = " + name );
    s.close();
    s = con.createStatement();
    s.executeUpdate( "INSERT INTO ShoppingCart VALUES ( " +
        customerID + ", " + bookID + " )" );
    s.close();
    con.commit();
} catch( SQLException x ) {
    System.err.println( "Update Failed, rolling back" );
    try { con.rollback() }
    catch( SQLException x ) { }
}

Understanding Distributed Transactions

Transactions against a single database are handled quite cleanly by the commit() and rollback() methods of the JDBC Connection object. An example of this was shown in Listing 16.1. J2EE applications often have much more complex transactions resulting from their distributed nature. For example, the same transaction may span multiple tiers, possibly shared by the servlet and EJB. Also, multiple databases may be involved in the same transaction. Such transactions are referred to as distributed transactions. The Java Transaction API (JTA) provides the ability to handle distributed transactions. The WebLogic services specifically designed for distributed transactions work with JTA, XA-compliant resources, and Two-Phase Commit (2PC) operations. These topics are addressed in the section “Performing Distributed Transactions with a Transaction Manager,” later in this chapter.

ACID-Compliant Databases

Cooperation occurs between the application server and the database server to ensure that transactions have basic properties that are referred to by the mnemonic ACID (see Table 16.1).

Table 16.1. Basic Database Properties Referred to by the Mnemonic ACID

Property

Description

Atomicity

Actions complete as a group or not at all.

Consistency

The state of the data storage remains consistent with the state of the business logic, whether the transaction succeeds or fails.

Isolation

Actions must run the same as if they were run serially.

Durability

Results of transactions must be stored persistently.

The database that you use with WebLogic Server must be ACID-compliant. The JDBC drivers offered by BEA WebLogic all connect to ACID-compliant databases.

Note

Any database used with an enterprise application must be ACID-compliant. For example, the MySQL database server added ACID compliance in September 2002 to enable development of e-commerce solutions to use MySQL. Prior to this release, MySQL could not be used with enterprise applications that required distributed transactions. Refer to the documentation provided by the database vendor for verification.

Whether the transaction is simple or distributed, the requirement is the same—to complete a set of database actions as a group, which is referred to as atomicity (refer to Table 16.1). To support transaction management, the database being used must be ACID-compliant. An ACID-compliant database can process the standard SQL commands that are used with transaction management; these commands are shown in Table 16.2.

Table 16.2. Standard SQL Commands Used with Transaction Management

Command

Action

BEGIN or START

Signifies to the database that a transaction is being created. All subsequent database updates are within the scope of this transaction.

PREPARE

Signifies to the database that a distributed transaction is about to be committed. This command is not used with simple transactions.

COMMIT

Signifies to the database to write the transaction to persistent storage.

ROLLBACK

Signifies to the database that the current transaction must be discarded.

The JDBC Optional Package and JTA

The JDBC Core API provides access to the database from Java applications, as shown in Chapter 13. The JDBC Optional Package and JTA supplement the JDBC Core API to address the requirements of distributed applications. The new functionality that is provided by the JDBC Optional Package includes

  • The DataSource interface to be used instead of the DriverManager

  • Connection pooling

  • Distributed transactions

  • RowSets

Two very significant features provided by the database driver vendor and the application server are distributed transactions and connection pooling. These features enable the portability, robustness, and data durability required by enterprise applications. As a J2EE developer, it is essential to have a basic understanding of distributed transactions and connection pooling. You can then incorporate these features into your J2EE applications using the JDBC Optional Package and JTA.

The DataSource interface is implemented by a driver vendor. The particular implementation of the DataSource allows the middle-tier infrastructure to provide connection pooling and distributed transactions. The DataSource therefore is implemented in one of three manners:

  • Basic implementation—Produces a standard Connection object.

  • Connection pooling implementation—Produces a Connection object that participates in connection pooling. Requires a middle-tier connection pooling manager.

  • Distributed transaction implementation—Produces a Connection object that may be used for distributed transactions. Requires a middle-tier distributed transaction manager and, almost always, a connection pooling manager.

The Java Transaction API provides functionality to allow an application to explicitly manage transaction boundaries. The interface that defines the methods for transaction management is the UserTransaction interface. Both the DataSource and UserTransaction objects are used directly by a server-side component of a J2EE application. This component is either a servlet or an Enterprise JavaBean. The objects that implement the DataSource or UserTransaction interface are registered with a naming service. The Java application obtains an instance of these objects using the JNDI API.

The API for the JDBC Optional Package is in the javax.sql package. The API for JTA is in the javax.transaction and javax.transaction.xa packages. The package names follow the Java naming convention, indicating that they are standard extensions to the Java programming language. They are included as part of the standard Java SDK 1.4 release. The server-side data source access provided by the JDBC Optional Package and the transaction management provided by JTA make these APIs essential features of the J2EE architecture.

Using the DataSource Interface

The DataSource interface is part of the JDBC Optional Package and is found in the javax.sql package. When used, the DataSource completely replaces the DriverManager and offers substantial benefits. As you saw in Chapter 13, the DriverManager requires you to register a JDBC driver and use the corresponding JDBC URL as the parameter to DriverManager.getConnection( "JDBC_URL" ). You lose portability because the DriverManager does not allow you to be database independent. Therefore, you are not really isolated from the database.

The JDBC Optional Package solved this issue by using JNDI to search for a DataSource object within a naming and directory service. The DataSource acts as a connection factory by containing all the information necessary to create a Connection instance to a particular database. The naming and directory service is used to match a name to the DataSource. Now, all the application needs to do is look up the name to obtain the DataSource and then use the DataSource object to get a Connection to the database. The naming and directory service is administered independently, allowing modifications to the database connection information without your having to change Java source code. See the section, “Configuring the WebLogic JDBC DataSource,” later in this chapter for further information on using the WebLogic Server Console to configure DataSources.

Your Java code to create a Connection now looks like this:

Context ctx = new InitialContext();
DataSource ds = (DataSource)ctx.lookup( "MY_DATASOURCE_NAME" );
Connection con = ds.getConnection( DATABASE_USER", "DATABASE_PASSWORD );

Using Connection Pools

A simple, straightforward database application opens a connection to the database, executes an update, and closes the connection. This mode of operation does not scale well when there are many database accesses per minute. A serious performance degradation would be evident because connecting to the database is an expensive operation. JDBC provides a standardized solution to this problem through connection pooling. A cache of open database connections is maintained by a middle-tier connection pool manager. The application gets a Connection instance from the connection pool, uses it, and then returns it to the connection pool. The database connection is never closed until the connection pool manager is shut down.

The API for connection pooling allows the application to treat standard Connection objects and connection pooling objects identically. There is absolutely no difference in the API. The key to getting a Connection from a connection pool is the DataSource interface that is part of the JDBC Optional Package. Rather than get the Connection object from the DriverManager, you get the Connection object from a DataSource object. The remainder of your JDBC application is exactly the same because you are still working with a Connection object; it is the same Connection object that was introduced in Chapter 13. The DataSource object is retrieved from the application server by performing a JNDI lookup on the name of the DataSource. Each DataSource is associated with a connection pool as part of its configuration. The connection pools must therefore be configured before creating the DataSource. Refer to the section “Configuring a WebLogic Connection Pool” later in this chapter for further information.

Using a DataSource for Distributed Transactions

The basic concepts of distributed transactions were covered earlier in this chapter. The Connection objects that are obtained from a DataSource that is implemented to work with a middle-tier transaction manager can participate in distributed transactions. The WebLogic application server provides the transaction manager through its JTA driver. The J2EE application deployed on WebLogic Platform must therefore use the WebLogic JTA driver when distributed transactions are required. The application does not do anything special with the Connection object to enable it to participate in distributed transactions. In fact, there is a restriction that the application cannot call commit(), rollback(), or setAutoCommit() on a Connection object that is under the control of a middle-tier transaction manager. Otherwise, the Connection API is the same. The UserTransaction interface in JTA defines methods that allow the application to explicitly manage transaction boundaries. The DataSource must be configured as a Tx DataSource on WebLogic Platform if it is to be used for distributed transactions. Refer to the section “Configuring a WebLogic Tx DataSource” later in this chapter.

Using the RowSet Interface

The RowSet interface is part of the JDBC Optional Package and is found in the javax.sql package. It adds JavaBeans component model support to the JDBC API and acts as a wrapper for JDBC and DataSource operations. The RowSet interface extends java.sql.ResultSet, therefore fully inheriting the API of ResultSet. Following the JavaBeans specification, the RowSet interface provides setter and getter methods to configure its properties. The RowSet interface also supports the JavaBeans event model for user interface events. The event model that is dictated by JavaBeans is implemented by having the JavaBean notify listeners of significant events through an event listener interface. Any object can implement the RowSetListener interface and add itself as a listener to the RowSet JavaBean. The RowSetListener provides the interface, and the RowSetEvent class provides the information as to which event occurred. These types of events are notified by a RowSet:

  • Indicating that the RowSet cursor has moved

  • Indicating that a row has been inserted, updated, or deleted

  • Indicating that the entire contents of the RowSet have changed, meaning an execute() has been performed

The RowSet interface is implemented either by the JDBC driver vendor or a third party. The implementing class is a wrapper for a DataSource, PreparedStatement, and a ResultSet. The following example uses the ImaginaryRowSet class that comes with the mSQL-JDBC driver. The configuration of the RowSet is as follows:

RowSet rs = new ImaginaryRowSet();         // create the RowSet object
rs.setDataSourceName( "MY_DATASOURCE" );   // configure the DataSource
rs.setUsername( "DATABASE_USER" );         // configure database user and password
rs.setPassword( "DATABASE_PASSWORD" );
rs.setCommand( "SELECT * FROM books " +    // configure the PreparedStatement
"WHERE title = ?" );

To use the RowSet object, first bind parameters to the fields in the Prepared statement; then invoke the execute() method. Because RowSet inherits from ResultSet, retrieving the data is exactly the same as with a ResultSet. The only difference is that RowSet sends out event notification through the listener interface. The following example shows how to use the configured RowSet:

rs.setString( "MY_FAVORITE_BOOK" );
rs.execute();
while( rs.next() )
    System.out.println( "Book id is " + rs.getInt( "book_id" ) ) ;

Using the UserTransaction Interface

The UserTransaction interface is part of JTA and is found in the javax.transaction package. As with the DataSource, the object that implements the UserTransaction is retrieved through JNDI from a naming and directory service. The name to look up is the full package name with the interface name, which is javax.transaction.UserInterface. The UserTransaction interface enables you to perform distributed transactions because the scope of the transaction is raised from the Connection object to the executing thread level.

Now you can start a transaction, locate and connect to as many DataSources as necessary, execute resource operations, close the connections, and then commit the transaction. This capability is essential for most enterprise systems where you need control over the transaction boundaries. The one restriction is that the DataSource must be a distributed transaction implementation, as mentioned previously. The transactional DataSource is implemented by the TxDataSource class. The main point to remember is not to specify a pool driver when locating the DataSource. The section “Configuring WebLogic JDBC Tx DataSources” later in this chapter shows how to configure a TxDataSource using the WebLogic Server Console.

The following is an example of locating and using an instance of a UserTransaction:

try {
Context ctx = new InitialContext();
UserTransaction tx = (UserTransaction)ctx.lookup( "javax.transaction.UserTransaction" );
tx.begin();    // start transaction
// locate DataSource, establish connection, execute operations, close connections
tx.commit();    // commit transactions
} catch( Exception x ) {
    System.err.println( "error has occurred" );
try {
    tx.rollback();
} catch( javax.transaction.SystemException se ) { }
    }

Configuring WebLogic JDBC Connection Pools

The connection pool is configured through the WebLogic Server Console. Figure 16.2 shows the WebLogic JDBC Connection Pools page for creating a new JDBC Connection Pool. Follow these steps to configure a new connection pool:

Connection Pools are configured through the WebLogic Server Console.

Figure 16.2. Connection Pools are configured through the WebLogic Server Console.

  1. Connect to your WebLogic Server Console using a web browser.

  2. Select Services/JDBC/Connection Pools from the navigation tree on the left side of the console.

  3. Click the Configure a New JDBC Connection Pool link.

  4. Fill in the form as described in Table 16.3.

  5. Click the Apply button.

Tip

As shown in Figure 16.2, each field in the form is preceded with a “?” character. Click on the “?” to view context sensitive help for the particular field.

Table 16.3. The Connection Pool Fully Encapsulates the Configuration for the DriverManager

Field Name

Description

Name:

Any name of your choosing as long as no other connection pools are using it.

URL:

This is the same URL that would be used as the parameter to getConnection() if you were directly using the DriverManager.

Driver Classname:

This is the same driver class name that would be used if you were directly creating the JDBC driver.

Properties (key=value):

This is used to specify the user for a password-protected database. The syntax is user=username. For example, the Oracle sample schema has a sample user named SCOTT; the property is user=SCOTT.

ACLName:

This is the name of the Access Control List for this connection pool.

 

(Only applicable if you are using the Compatibility realm to adapt your WebLogic Server 6.x Authentication and Authorization providers to be compatible with the WebLogic Server 7.0. In WebLogic Server 7.0 ACLs are deprecated and are replaced by security policies.)

Password:

This is the password for the user given in the Properties field. For example, the password for SCOTT in the Oracle sample schema is tiger.

Open String Password:

If set, this value is used to override the password in the open string.

Configuring WebLogic JDBC Tx DataSources

The data source is configured through the WebLogic Server Console. The Tx DataSources support distributed transaction where standard DataSources do not. Figure 16.3 shows the WebLogic JDBC Tx DataSources page to create a new JDBC Tx DataSource. Follow these steps to configure a new Tx DataSource:

  1. Connect to your WebLogic Server Console using a web browser.

  2. Select Services/JDBC/Tx DataSources from the navigation tree on the left side of the console.

  3. Click the “Configure a new JDBC Tx DataSource...” link.

  4. Fill in the form as described in Table 16.4.

  5. Click the Apply button.

Tip

As shown in Figure 16.3, each field in the form is preceded with a “?” character. Click on the “?” to view context-sensitive help for the particular field.

Tx DataSources are configured through the WebLogic Server Console to support distributed transactions.

Figure 16.3. Tx DataSources are configured through the WebLogic Server Console to support distributed transactions.

Table 16.4. The Tx DataSource Is Configured Through the WebLogic Server Console

Field Name

Description

Name:

Any name of your choosing as long as no other data sources are using it.

JNDI Name:

This is the name that the application will lookup using JNDI to get this data source.

Pool Name:

This is the name of a JDBC connection pool that is associated with this Tx DataSource.

Emulate Two-Phase Commit for non-XA Driver: (check box)

Set this field if there is no XA-compliant JDBC driver available for the database you are using and you are forced to emulate distributed transactions.

Row Prefetch Enabled: (check box)

This option is used to improve performance by allowing the WebLogic Server to prefetch multiple rows for a ResultSet.

Row Prefetch Size:

This is the number of rows that will be prefetched for a ResultSet. The default value is 48, the minimum is 2, and the maximum is 65536. It is not recommended to exceed 100 for the prefetch size because additional performance is rarely achieved above this level.

Stream Chunk Size:

This is the number of bytes that WebLogic Server will read from a data stream for methods such as getBinaryStream(). The default value is 256, the minimum is 1, and the maximum is 65536.

Performing Distributed Transactions with a Transaction Manager

The middle tier of the JTA architecture is the Transaction Manager. The responsibility of the Transaction Manager is to manage the communication between the application and XA-Compliant resources. The transaction manager provides the ability to perform distributed transactions by coordinating and controlling the resources that are participating in the transaction. The application starts a transaction with the transaction manager, and then opens and communicates with the Tx DataSource objects that are participating in the distributed transaction. The application finally requests the transaction manager to commit all data storage modifications. The transaction manager performs a Two-Phase Commit operation, as explained in the “Two-Phase Commit (2PC)” section later in this chapter. If the transaction manager is unable to commit the transaction to all the participating resources, it will roll back the entire distributed transaction.

Note

The WebLogic JTA driver must be used to perform distributed transactions on WebLogic Platform. Refer to “Using the WebLogic JTA Driver” later in this chapter for further information.

XA-Compliant Resources

XA compliance refers to the X/Open XA interface specification, which defines the communication between a transaction manager and one or more resource managers. The role of the transaction manager is to coordinate and control all the resource managers that are participating in a distributed transaction. The X/Open standards body developed the Distributed Transaction Processing (DTP) model and the XA interface to solve the problems of distributed transactions. The most significant problem is how to coordinate the commit or rollback of a transaction on distributed individual resources. The transaction manager ensures that all resources successfully complete the transaction, or all operations fail as a group. The JTA driver running in the WebLogic Server provides the transaction management for the distributed transaction. All database servers participating in the distributed transaction must therefore implement an XA-compliant resource manager.

Two-Phase Commit (2PC)

The WebLogic JTA driver, acting as the transaction manager, and the database servers, acting as resource managers, use the Two-Phase Commit protocol with presumed rollback. That is, if something goes wrong during the transaction, each resource manager automatically rolls back its portion of the transaction. In phase one, the transaction manager asks all resource managers to prepare to commit a transaction. If a resource manager is unable to commit, it automatically rolls back any work it performed on behalf of the transaction. In phase two, the transaction manager determines whether any resource managers were unable to commit. Based on the results of phase one, preparing to commit, the transaction manager instructs the resource managers to either commit or roll back.

Transaction Isolation Levels

The ACID properties require that database operations and transactions must be isolated from concurrency issues. The operations must complete with identical results whether being performed serially or concurrently. The JDBC specification provides Transaction Isolation Levels that allow the application to control the behavior of the database for concurrency issues. There are five levels, which are described in Table 16.5; however, you must review your JDBC driver and database documentation to determine which levels are available.

The transaction isolation levels guard against three conditions that may occur with concurrent access to a database:

  • Dirty reads—Refers to reading data that was written by another uncommitted transaction.

  • Non-repeatable reads—Occurs when a transaction rereads data and finds that the data has been modified by another transaction that was committed since the initial read.

  • Phantom reads—Occurs when a query is re-executed and the results are different because another transaction has committed changes to the data.

Table 16.5. The Five Transaction Isolation Levels

Level

Action

TRANSACTION_NONE

Makes no attempt to isolate the transaction from another transaction.

TRANSACTION_READ_UNCOMMITTED

Allows dirty reads, meaning that it selects the latest value whether or not it has been committed from another transaction.

TRANSACTION_READ_COMMITTED

Does not allow dirty reads, meaning that it always selects the most recently committed data. Warning: This level does not guarantee repeatable reads because new data may be committed between the read operations.

TRANSACTION_REPEATABLE_READ

Does not allow dirty or nonrepeatable reads. Warning: This level does not guarantee protection against phantom reads because new data may be committed to other tables with relationships to this table. REPEATABLE_READ is rarely used because the performance costs outweigh its limited benefits.

TRANSACTION_SERIALIZABLE

Does not allow dirty, nonrepeatble, or phantom reads. This is a trade-off between high reliability and low performance.

To set the Transaction Isolation Level, call the method setTransactionIsolation() on the Connection object. The parameter is one of the five public static constants defined in the Connection class. The field names are exactly the same as shown in Table 16.5.

Using the WebLogic JTA Driver

The WebLogic JTA driver provides a full distributed transaction implementation of the DataSource interface. The JTA driver also supports connection pooling. To enhance performance, the WebLogic JTA driver uses standard SQL commands to the database when the scope of the transaction is limited to a single DataSource.

To provide all the ACID properties between WebLogic Server and the database server, the transaction services provided by the JTA driver require that all resources involved in the transaction are XA-compliant. When multiple DataSources are involved in the transaction, the JTA driver uses a Two-Phase Commit (2PC) engine. Finally, distributed transactions introduce race conditions and concurrency issues that must be dealt with. This refers to the Isolation property of ACID. For example, two separate transactions might try to update the same data at the same time. JDBC specifies Transaction Isolation Levels that allow you to control how the database behaves in these situations. Each of these topics were introduced in the preceding sections.

The BookStore Servlet Example

Listing 16.2 shows a complete example using JDBC and JTA from a servlet deployed on the WebLogic Platform. The BookStoreServlet provides the user interface for a Web-based application to search for and order books. This example focuses on using JDBC with JTA. Refer to Chapter 19 “Servlets and JavaServer Pages—Best Practices” for more information on servlets. The init() method in the BookStoreServlet connects to the WebLogic JNDI naming service. The JNDI connection is used to lookup the DataSource for the relational database. The service() method is invoked when the HTML form that is handled by this servlet is submitted. The first step in the service() method is to lookup the JTA UserTransaction in the WebLogic naming service. All further processing is performed within a transaction which is either committed, if all completes successfully, or rolled back, if an error occurs. The following actions are performed within the transaction:

  1. Parse the user name and book title from the HttpServletRequest.

  2. Query the database to find the customer ID and balance of available funds.

  3. Query the database to find the book ID, price, and number of available copies in inventory.

  4. Update the books table to decrement the number of copies for the book being purchased.

  5. Update the customers table to decrement the customer’s balance by the price of the book.

  6. Commit the transaction if no exceptions are thrown.

  7. Roll back the transaction if an exception is thrown.

The BookStoreServlet checks the inventory for the number of available copies of the book and the customer’s balance to verify that the balance is greater than the price of the book. If the book is out of stock or the customer’s balance is less than the price of the book, throw an exception which will roll back the transaction. These checks are performed in queryBook() and queryCustomer() respectively.

This example was created using Together ControlCenter version 6.0. The class diagram was created using the Class By Pattern tool and choosing the Servlet pattern. For the pattern properties, we chose Implement Method init() and Implement Method service(). Together they generate the class diagram and template source code. The remainder of the application was manually edited within the Java source code.

The class diagram of the BookStore servlet shows the attributes and methods for the class.

Figure 16.4. The class diagram of the BookStore servlet shows the attributes and methods for the class.

Example 16.2. Template Source Code for the BookStore Servlet Generated by Together ControlCenter 6.0

/* Generated by Together */
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.Statement;
import javax.transaction.UserTransaction;

public class BookStoreServlet extends HttpServlet  {
    // constants
    private static final String DATASOURCE_NAME = "datasource-demopool";
    private void parseForm( HttpServletRequest req ) {
        Enumeration fields = req.getParameterNames();
        while( fields.hasMoreElements() ) {
            String field = (String)fields.nextElement();
            if( field.equals( "name" ) )
                name = req.getParameter( field );
            else if( field.equals( "title" ) )
                title = req.getParameter( field );
        }
    }
    public void init(ServletConfig config)throws ServletException{
        super.init(config);
        //
        //Write your code here
        //
        name = null;
        title = null;
        try {
            // create a connection to the WebLogic JNDI Naming Service
            ctx = new InitialContext();
            // locate a DataSource in the Naming Service
            ds = (DataSource)m_ctx.lookup( DATASOURCE_NAME );
        }
        catch( Exception x ) {
            System.err.println( "Error in init(): " + x );
        }
    }

    public String getServletInfo(){
        return "Servlet description";
    }

    public void service(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {
        //
        //Write your code here
        //
        Connection con = null;
        UserTransaction tx = null;

        try {
            PrintWriter out = res.getWriter();
            // print the HTML header
            out.println( "<html>" );
            out.println( "<head><title>Book Store Servlet</title></head>" );
            out.println( "<body>" );
            out.println( "<h1>Book Store Servlet</h1>" );
            // locate a UserTransaction in the Naming service
            tx = (UserTransaction)ctx.lookup(
                "javax.transaction.UserTransaction" );
            // start a transaction
            tx.start();
            // get a JDBC Connection from the DataSource
           con = ds.getConnection();
            // create a Statement
            Statement st = con.createStatement();
            // get customer name and book name from the HTML form
            parseForm( req );
            // search database for requested data
            queryBook( con );
            queryCustomer( con );
            // update the database
            con.setAutoCommit( false );    // disable auto-commit
            st.executeUpdate( "UPDATE books SET copies = " + ( copies - 1 ) +
                " WHERE title = " + title );
            st.close();
            st = con.createStatement();
            st.executeUpdate( "UPDATE customers SET balance = " +
                ( balance - price ) +
                " WHERE customer_name = " + name );
            st.close();
            st = con.createStatement();
            st.executeUpdate( "INSERT INTO ShoppingCart VALUES ( " +
                customerID + ", " + bookID + " )" );
            st.close();
            // commit the transaction
            tx.commit();
            out.println( "<h2>Success</h2>" );
            out.println( "</body>" );
            out.println( "</html>" );
        }
        catch(Exception x ) {
            System.err.println( "Rolled back transaction: " + x.toString() );
            tx.rollback();
        }
    }
    private void queryCustomer( Connection con, float price )
        throws SQLException, Exception {
        Statement st = con.createStatement();
        ResultSet rs = st.executeQuery(
            "Select * from customers WHERE customer_name = " + name  );
        rs.next();
        customerID = rs.getInt( "customer_id" );
        balance = rs.getFloat( "balance" );
    // verify balance is greater than price of book
    if( balance < price )
    {
       throw new Exception( name + " has insufficient funcds" );
    }
    }
    private void queryBook( Connection con )
        throws SQLException, Exception {
        Statement st = con.createStatement();
        ResultSet rs = st.executeQuery(
            "Select * from books WHERE title = " + title );
        rs.next();
        bookID = rs.getInt( "book_id" );
        price = rs.getFloat( "price" );
        copies = rs.getInt( "copies" );
    // verify there is at least 1 copy of the book
    if( copies == 0 )
    {
        throw new Exception( title + " is out of stock" );
    }
    }
    private Context ctx;
    private DataSource ds;

    private String name;
    private int customerID;
    private float balance;

    private String title;
    private int bookID;
    private int copies;
    private float price;
}

Summary

The WebLogic Application Server with the JTA driver provides full support for J2EE applications that require distributed transactions. Best of all, it is implemented transparently to the application. The JTA driver provides the transaction manager, and the XA-compliant resources provide the resource managers. The J2EE application need only locate the UserTransaction and DataSource objects in the naming and directory service using JNDI and start the transaction. After the Connection object is obtained from the DataSource, access to the data using JDBC is exactly the same. After you successfully perform all the updates, invoke the commit() method on the UserTransaction object. If any exceptions are thrown, indicating an error condition, you can perform a rollback.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset