Overview
A transaction encapsulates a sequence of operations that are treated as a single atomic unit. The results of the operations within a transaction are committed only if all the operations are completed successfully. Otherwise, the transaction is rolled back and all changes resulting from the operations are abandoned. Regardless of whether the changes are committed or rolled back, the resources participating in the transaction must always be left in a consistent state. Intermediate changes made during the lifetime of a transaction are isolated from the outside world they remain invisible until the transaction has completed. A transaction, if completed successfully, must ensure the changes are persistent or durable.
WebLogic Server provides the infrastructure for transaction management and guarantees the integrity of your transactions. As a TP monitor, WebLogic can manage transactions and coordinate access across various enterprise stores, including a relational database, JMS servers, or any transaction-aware resource. WebLogic guarantees that updates to multiple resources occur correctly and accurately when a transaction completes successfully, and the entire transaction rolls back if any of the operations fail.
6.1.1 JDBC Transactions
The JDBC API supports local transactions, where the scope of the JDBC transaction does not extend beyond the lifetime of the associated JDBC connection. If a connection has auto-commit enabled, the JDBC driver is forced to commit any changes made after the completion of every SQL statement. In effect, every SQL statement executed against the Connection object is encapsulated in a transaction. If auto-commit is disabled for a connection, the JDBC client is required to manually commit the transaction by invoking the commit( ) method on the Connection object. Alternatively, the JDBC client can indicate failure and drop all changes made by calling the rollback( ) method on the Connection object. The commit( ) and rollback( ) methods also are required to release any database locks held by the associated Connection object.
An orthogonal issue to JDBC transactions is the level of concurrent access from within a current transaction i.e., the isolation level between multiple transactions. There are three ways in which two concurrent transactions can interact:
Dirty reads
These occur when Transaction A can see uncommitted changes to data made by Transaction B.
Nonrepeatable reads
These occur in the following scenario: Transaction A reads a row, Transaction B modifies the same row, and then Transaction A rereads the updated row and gets differing results.
Phantom reads
Phantom reads can occur in the following situation: Transaction A reads rows that satisfy a WHERE clause, Transaction B inserts additional rows that satisfy the same search criteria, and then Transaction A reevaluates the WHERE clause to get new "phantom" rows.
A JDBC driver can support the following isolation levels, depending on the kind of interactions you would like to allow:
TRANSACTION_READ_UNCOMMITTED
A transaction is allowed to read uncommitted changes to data.
TRANSACTION_READ_COMMITTED
Any changes within a transaction remain invisible to the outside world, until the transaction is committed.
TRANSACTION_REPEATABLE_READ
Dirty and nonrepeatable reads are disallowed, but phantom reads may occur.
TRANSACTION_SERIALIZABLE
This is the most restrictive isolation level, and emulates serial execution of both transactions. Dirty, nonrepeatable, and phantom reads are disallowed.
The JDBC driver determines the default isolation level for a connection. Typically, it is the default isolation level for the underlying database. Use the setTransactionIsolation( ) method to adjust the isolation level for a Connection object. A JDBC driver that doesn't support a particular isolation level can replace it with a higher, more restrictive isolation level. The higher the isolation level, the lower the degree of concurrent access. So, the isolation level for a connection has a direct impact on the performance of your applications. If a JDBC driver does not support transactions, the getTransactionIsolation( ) method on the Connection object returns the value TRANSACTION_NONE.
6.1.2 Distributed Transactions
A distributed transaction involves updates that span multiple connections to resource managers (databases, EJBs, JMS, etc.). A transaction manager coordinates these updates across multiple resource managers using the two-phase commit protocol. Here are the major players involved in a distributed transaction:
- A transactional application is one that initiates the transaction context. This could be a client Java application, the EJB container, or even a JMS message producer.
- A transaction manager maintains the transactional boundaries on behalf of the application and coordinates all resource managers participating in a transaction.
- A resource manager manages the underlying data. In the context of JDBC, the DBMS server is the resource manager.
The two-phase commit protocol involves the following two phases:
A prepare phase
All updates involved are recorded in a transaction log, and each resource manager (on behalf of the resource) indicates whether it can make the requested change.
A commit phase
If all resource managers vote to commit, all the resources participating in the transaction are updated permanently. If any resource manager votes to roll back, all the resources participating in the transaction roll back to their previous consistent state.
WebLogic Server provides full transaction support for applications that use EJBs or the JTA interface.
A J2EE application that needs support for distributed transactions must use the JTA. Later in this chapter, in Section 6.3.1, we look at an example of how an application can employ JTA transactions.
If an application performs JDBC updates as part of a distributed transaction, it must use transaction-aware data sources. Thus, you need to interface with the configured connection pool using an XA-aware data source that has been bound to the JNDI tree. You must use transactional data sources especially if your application uses enterprise beans that support container-managed transactions. If the JDBC driver is not XA-compliant and your transactions involve updates to other resources as well, you must enable two-phase commit emulation for the XA-aware data source. Only then can a connection obtained from the data source participate in XA transactions.