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.
▸ For further information on Java Database Connectivity see Chapter 13, p. 403.
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 scenario for a customer to purchase a book is as follows:
Customer identifies himself by customer_name
.
Customer requests to purchase a book by title
.
Verify there is at least one copy of the book.
Verify customer has enough money in his balance to cover the price of the book.
Decrement the copies of the book by one.
Decrement the customer balance by the price of the book.
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 ) { } }
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.
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 |
---|---|
Actions complete as a group or not at all. | |
The state of the data storage remains consistent with the state of the business logic, whether the transaction succeeds or fails. | |
Actions must run the same as if they were run serially. | |
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.
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
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.
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 );
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.
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.
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" ) ) ;
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 DataSource
s 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 ) { } }
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:
Connect to your WebLogic Server Console using a web browser.
Select Services/JDBC/Connection Pools from the navigation tree on the left side of the console.
Click the Configure a New JDBC Connection Pool link.
Fill in the form as described in Table 16.3.
Click the Apply button.
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
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:
Connect to your WebLogic Server Console using a web browser.
Select Services/JDBC/Tx DataSources from the navigation tree on the left side of the console.
Click the “Configure a new JDBC Tx DataSource...” link.
Fill in the form as described in Table 16.4.
Click the Apply button.
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.
Table 16.4. The Tx DataSource Is Configured Through the WebLogic Server Console
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.
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 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.
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.
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
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.
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 DataSource
s 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.
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:
Parse the user name and book title from the HttpServletRequest
.
Query the database to find the customer ID and balance of available funds.
Query the database to find the book ID, price, and number of available copies in inventory.
Update the books table to decrement the number of copies for the book being purchased.
Update the customers table to decrement the customer’s balance by the price of the book.
Commit the transaction if no exceptions are thrown.
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.
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; }
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.