Skip Headers

Oracle® Database JDBC Developer's Guide and Reference
10g Release 1 (10.1)

Part Number B10979-01
Go to Documentation Home
Home
Go to Book List
Book List
Go to Table of Contents
Contents
Go to Index
Index
Go to Master Index
Master Index
Go to Feedback page
Feedback

Go to previous page
Previous
Go to next page
Next
View PDF

9 Distributed Transactions

This chapter discusses the Oracle JDBC implementation of distributed transactions. These are multi-phased transactions, often using multiple databases, that must be committed in a coordinated way. There is also related discussion of XA, which is a general standard (not specific to Java) for distributed transactions.

The following topics are discussed:

For further introductory and general information about distributed transactions, refer to the Sun Microsystems specifications for the JDBC 2.0 Optional Package and the Java Transaction API (JTA).

For information on the OCI-specific HeteroRM XA feature, see "OCI HeteroRM XA".

Overview

A distributed transaction, sometimes referred to as a global transaction, is a set of two or more related transactions that must be managed in a coordinated way. The transactions that constitute a distributed transaction might be in the same database, but more typically are in different databases and often in different locations. Each individual transaction of a distributed transaction is referred to as a transaction branch.

For example, a distributed transaction might consist of money being transferred from an account in one bank to an account in another bank. You would not want either transaction committed without assurance that both will complete successfully.

In the JDBC 2.0 extension API, distributed transaction functionality is built on top of connection pooling functionality. This distributed transaction functionality is also built upon the open XA standard for distributed transactions. (XA is part of the X/Open standard and is not specific to Java.)

JDBC is used to connect to database resources. However, to include all changes to multiple databases within a transaction, you must use the JDBC connections within a JTA global transaction. The process of including database SQL updates within a transaction is referred to as enlisting a database resource.

The remainder of this overview covers the following topics:

For further introductory and general information about distributed transactions and XA, refer to the Sun Microsystems specifications for the JDBC 2.0 Optional Package and the Java Transaction API.

Distributed Transaction Components and Scenarios

In reading the remainder of the distributed transactions section, it will be helpful to keep the following points in mind:

  • A distributed transaction system typically relies on an external transaction manager—such as a software component that implements standard Java Transaction API functionality—to coordinate the individual transactions.

    Many vendors offer XA-compliant JTA modules, including Oracle, which includes JTA in Oracle 9iAS and Oracle Application Server 10g.

  • XA functionality is usually isolated from a client application, being implemented instead in a middle-tier environment such as an application server.

    In many scenarios, the application server and transaction manager will be together on the middle tier, possibly together with some of the application code as well.

  • Discussion throughout this section is intended mostly for middle-tier developers.

  • The term resource manager is often used in discussing distributed transactions. A resource manager is simply an entity that manages data or some other kind of resource. Wherever the term is used in this chapter, it refers to a database.


    Note:

    Using JTA functionality requires file jta.jar to be in the CLASSPATH. (This file is located at ORACLE_HOME/jlib.) Oracle includes this file with the JDBC product. (You can also obtain it from the Sun Microsystems Web site, but it is advisable to use the version from Oracle, because that has been tested with the Oracle drivers.)

Distributed Transaction Concepts

When you use XA functionality, the transaction manager uses XA resource instances to prepare and coordinate each transaction branch and then to commit or roll back all transaction branches appropriately.

XA functionality includes the following key components:

  • XA datasources—These are extensions of connection pool datasources and other datasources, and similar in concept and functionality.

    There will be one XA datasource instance for each resource manager (database) that will be used in the distributed transaction. You will typically create XA datasource instances (using the class constructor) in your middle-tier software.

    XA datasources produce XA connections.

  • XA connections—These are extensions of pooled connections, and similar in concept and functionality. An XA connection encapsulates a physical database connection; individual connection instances are temporary handles to these physical connections.

    An XA connection instance corresponds to a single Oracle session, although the session can be used in sequence by multiple logical connection instances (one at a time), as with pooled connection instances.

    You will typically get an XA connection instance from an XA datasource instance (using a getXAConnection method) in your middle-tier software. You can get multiple XA connection instances from a single XA datasource instance if the distributed transaction will involve multiple sessions (multiple physical connections) in the same database.

    XA connections produce XA resource instances and JDBC connection instances.

  • XA resources—These are used by a transaction manager in coordinating the transaction branches of a distributed transaction.

    You will get one XA resource instance from each XA connection instance (using a getXAResource method), typically in your middle-tier software. There is a one-to-one correlation between XA resource instances and XA connection instances; equivalently, there is a one-to-one correlation between XA resource instances and Oracle sessions (physical connections).

    In a typical scenario, the middle-tier component will hand off XA resource instances to the transaction manager, for use in coordinating distributed transactions.

    Because each XA resource instance corresponds to a single Oracle session, there can be only a single active transaction branch associated with an XA resource instance at any given time. There can be additional suspended transaction branches, however—see "XA Resource Method Functionality and Input Parameters".

    Each XA resource instance has the functionality to start, end, prepare, commit, or roll back the operations of the transaction branch running in the session with which the XA resource instance is associated.

    The "prepare" step is the first step of a two-phase COMMIT operation. The transaction manager will issue a prepare to each XA resource instance. Once the transaction manager sees that the operations of each transaction branch have prepared successfully (essentially, that the databases can be accessed without error), it will issue a COMMIT to each XA resource instance to commit all the changes.

  • Transaction IDs—These are used to identify transaction branches. Each ID includes a transaction branch ID component and a distributed transaction ID component—this is how a branch is associated with a distributed transaction. All XA resource instances associated with a given distributed transaction would have a transaction ID that includes the same distributed transaction ID component.

Switching Between Global and Local Transactions

As of JDBC 3.0, applications can share connections between local and global transactions. Applications can also switch connections between local transactions and global transactions.

A connection is always in one of three modes: NO_TXN, LOCAL_TXN, or GLOBAL_TXN.

  • NO_TXN —no transaction is actively using this connection.

  • LOCAL_TXN—a local transaction with auto-commit turned off or disabled is actively using this connection.

  • GLOBAL_TXN—a global transaction is actively using this connection.

Each connection switches automatically between these modes depending on the operations executed on the connection. A connection is always in NO_TXN mode when it is instantiated.

Table 9-1 Connection Mode Transitions

Current Mode Switches To NO_TXN When Switches to LOCAL_TXN When Switches To GLOBAL_TXN When
NO_TXN n/a Auto-commit mode is false and an Oracle DML (SELECT, INSERT, UPDATE) statement is executed start() is invoked on an XAResource obtained from the XAconnection that provided this connection
LOCAL_TXN Any of the following happens:

An Oracle DDL statement (CREATE, DROP, RENAME, ALTER) is executed.

commit() is invoked.

rollback() is invoked (parameterless version only).

n/a NEVER
GLOBAL_TXN Within a global transaction open on this connection, end() is invoked on an XAResource obtained from the XAconnection that provided this connection. NEVER n/a

If none of the rules above is applicable, the mode does not change.

Mode Restrictions On Operations

The current connection mode restricts which operations are valid within a transaction.

  • In LOCAL_TXN mode, applications must not invoke prepare(), commit(), rollback(), forget(), or end() on an XAResource. Doing so causes an XAException to be thrown.

  • In GLOBAL_TXN mode, applications must not invoke commit(), rollback() (both versions), setAutoCommit(), or setSavepoint() on a java.sql.Connection, and must not invoke OracleSetSavepoint() or oracleRollback() on an oracle.jdbc.OracleConnection. Doing so causes an SQLException to be thrown.


    Note:

    This mode-restriction error checking is in addition to the standard error checking on the transaction and savepoint APIs, documented in this chapter and in "Transaction Savepoints" .

Oracle XA Packages

Oracle supplies the following three packages that have classes to implement distributed transaction functionality according to the XA standard:

  • oracle.jdbc.xa (OracleXid and OracleXAException classes)

  • oracle.jdbc.xa.client

  • oracle.jdbc.xa.server

Classes for XA datasources, XA connections, and XA resources are in both the client package and the server package. (An abstract class for each is in the top-level package.) The OracleXid and OracleXAException classes are in the top-level oracle.jdbc.xa package, because their functionality does not depend on where the code is running.

In middle-tier scenarios, you will import OracleXid, OracleXAException, and the oracle.jdbc.xa.client package.

If you intend your XA code to run in the target Oracle database, however, you will import the oracle.jdbc.xa.server package instead of the client package.

If code that will run inside a target database must also access remote databases, then do not import either package—instead, you must fully qualify the names of any classes that you use from the client package (to access a remote database) or from the server package (to access the local database). Class names are duplicated between these packages.

XA Components

This section discusses the XA components—standard XA interfaces specified in the JDBC 2.0 Optional Package, and the Oracle classes that implement them. The following topics are covered:

XA Datasource Interface and Oracle Implementation

The javax.sql.XADataSource interface outlines standard functionality of XA datasources, which are factories for XA connections. The overloaded getXAConnection() method returns an XA connection instance and optionally takes a user name and password as input:

public interface XADataSource
{
   XAConnection getXAConnection() throws SQLException;
   XAConnection getXAConnection(String user, String password)
      throws SQLException;
   ...
}

Oracle JDBC implements the XADataSource interface with the OracleXADataSource class, located both in the oracle.jdbc.xa.client package and the oracle.jdbc.xa.server package.

The OracleXADataSource classes also extend the OracleConnectionPoolDataSource class (which extends the OracleDataSource class), so include all the connection properties described in "DataSource Properties".

The OracleXADataSource class getXAConnection() methods return the Oracle implementation of XA connection instances, which are OracleXAConnection instances (as the next section discusses).


Note:

You can register XA datasources in JNDI using the same naming conventions as discussed previously for non-pooling datasources in "Register the Datasource".

XA Connection Interface and Oracle Implementation

An XA connection instance, as with a pooled connection instance, encapsulates a physical connection to a database. This would be the database specified in the connection properties of the XA datasource instance that produced the XA connection instance.

Each XA connection instance also has the facility to produce the XA resource instance that will correspond to it for use in coordinating the distributed transaction.

An XA connection instance is an instance of a class that implements the standard javax.sql.XAConnection interface:

public interface XAConnection extends PooledConnection
{
   javax.jta.xa.XAResource getXAResource() throws SQLException;
}

As you see, the XAConnection interface extends the javax.sql.PooledConnection interface, so it also includes the getConnection(), close(), addConnectionEventListener(), and removeConnectionEventListener() methods.

Oracle JDBC implements the XAConnection interface with the OracleXAConnection class, located both in the oracle.jdbc.xa.client package and the oracle.jdbc.xa.server package.

The OracleXAConnection classes also extend the OraclePooledConnection class.

The OracleXAConnection class getXAResource() method returns the Oracle implementation of an XA resource instance, which is an OracleXAResource instance (as the next section discusses). The getConnection() method returns an OracleConnection instance.

A JDBC connection instance returned by an XA connection instance acts as a temporary handle to the physical connection, as opposed to encapsulating the physical connection. The physical connection is encapsulated by the XA connection instance.

Each time an XA connection instance getConnection() method is called, it returns a new connection instance that exhibits the default behavior, and closes any previous connection instance that still exists and had been returned by the same XA connection instance. It is advisable to explicitly close any previous connection instance before opening a new one, however.

Calling the close() method of an XA connection instance closes the physical connection to the database. This is typically performed in the middle tier.

XA Resource Interface and Oracle Implementation

The transaction manager uses XA resource instances to coordinate all the transaction branches that constitute a distributed transaction.

Each XA resource instance provides the following key functionality, typically invoked by the transaction manager:

  • It associates and disassociates distributed transactions with the transaction branch operating in the XA connection instance that produced this XA resource instance. (Essentially, associates distributed transactions with the physical connection or session encapsulated by the XA connection instance.) This is done through use of transaction IDs.

  • It performs the two-phase COMMIT functionality of a distributed transaction to ensure that changes are not committed in one transaction branch before there is assurance that the changes will succeed in all transaction branches.

    "XA Resource Method Functionality and Input Parameters" further discusses this.


    Notes:

    • Because there must always be a one-to-one correlation between XA connection instances and XA resource instances, an XA resource instance is implicitly closed when the associated XA connection instance is closed.

    • If a transaction is opened by a given XA resource instance, it must also be closed by the same XA resource instance.


An XA resource instance is an instance of a class that implements the standard javax.transaction.xa.XAResource interface:

public interface XAResource
{
   void commit(Xid xid, boolean onePhase) throws XAException;
   void end(Xid xid, int flags) throws XAException;
   void forget(Xid xid) throws XAException;
   int prepare(Xid xid) throws XAException;
   Xid[] recover(int flag) throws XAException;
   void rollback(Xid xid) throws XAException;
   void start(Xid xid, int flags) throws XAException;
   boolean isSameRM(XAResource xares) throws XAException;
}

Oracle JDBC implements the XAResource interface with the OracleXAResource class, located both in the oracle.jdbc.xa.client package and the oracle.jdbc.xa.server package.

The Oracle JDBC driver creates and returns an OracleXAResource instance whenever the OracleXAConnection class getXAResource() method is called, and it is the Oracle JDBC driver that associates an XA resource instance with a connection instance and the transaction branch being executed through that connection.

This method is how an OracleXAResource instance is associated with a particular connection and with the transaction branch being executed in that connection.

XA Resource Method Functionality and Input Parameters

The OracleXAResource class has several methods to coordinate a transaction branch with the distributed transaction with which it is associated. This functionality usually involves two-phase COMMIT operations.

A transaction manager, receiving OracleXAResource instances from a middle-tier component such as an application server, typically invokes this functionality.

Each of these methods takes a transaction ID as input, in the form of an Xid instance, which includes a transaction branch ID component and a distributed transaction ID component. Every transaction branch has a unique transaction ID, but transaction branches belonging to the same global transaction have the same global transaction component as part of their transaction IDs.

"XA ID Interface and Oracle Implementation" discusses the OracleXid class and the standard interface upon which it is based.

Following is a description of key XA resource functionality, the methods used, and additional input parameters. Each of these methods throws an XA exception if an error is encountered. See "XA Exception Classes and Methods".

Start

Start work on behalf of a transaction branch, associating the transaction branch with a distributed transaction.

void start(Xid xid, int flags)

The flags parameter must be one of the following values:

  • XAResource.TMNOFLAGS (no special flag)—Flag the start of a new transaction branch for subsequent operations in the session associated with this XA resource instance. This branch will have the transaction ID xid, which is an OracleXid instance created by the transaction manager. This will map the transaction branch to the appropriate distributed transaction.

  • XAResource.TMJOIN—Join subsequent operations in the session associated with this XA resource instance to the existing transaction branch specified by xid.

  • XAResource.TMRESUME—Resume the transaction branch specified by xid. (It must first have been suspended.)

  • OracleXAResource.ORATMSERIALIZABLE—Start a serializable transaction with transaction ID xid.

  • OracleXAResource.ORATMREADONLY—Start a read-only transaction with transaction ID xid.

  • OracleXAResource.ORATMREADWRITE—Start a read/write transaction with transaction ID xid.

  • OracleXAResource.ORATRANSLOOSE—start a loosely-coupled transaction with transaction ID xid.

TMNOFLAGS, TMJOIN, TMRESUME, ORATMSERIALIZABLE, ORATMREADONLY, and ORATMREADWRITE are defined as static members of the XAResource interface and OracleXAResource class. ORATMSERIALIZABLE, ORATMREADONLY, and ORATMREADWRITE are the isolation-mode flags. The default isolation behavior is READ COMMITTED.


Notes:

  • Instead of using the start() method with TMRESUME, the transaction manager can cast to an OracleXAResource instance and use the resume(Xid xid) method, an Oracle extension.
  • If you use TMRESUME, you must also use TMNOMIGRATE, as in start(xid, XAResource.TMRESUME | OracleXAResource.TMNOMIGRATE);. This prevents the application's receiving the error ORA 1002: fetch out of sequence.

  • If you use the isolation-mode flags incorrectly, an exception with code XAER_INVAL is raised. Furthermore, you cannot use isolation-mode flags when resuming a global transaction, because you cannot set the isolation level of an existing transaction. If you try to use the isolation-mode flags when resuming a transaction, an external Oracle exception with code ORA-24790 is raised.

  • In order to avoid Error ORA 1002: fetch out of sequence, include the TMNOMIGRATE flag as part of the start method. For example:

    start(xid, XAResource.TMSUSPEND | OracleXAResource.TMNOMIGRATE);
    
  • All the flags defined in OracleXAResource are Oracle extensions. When writing a transaction manager that uses these flags, you should be mindful of this.


Note that to create an appropriate transaction ID in starting a transaction branch, the transaction manager must know which distributed transaction the transaction branch should belong to. The mechanics of this are handled between the middle tier and transaction manager and are beyond the scope of this document. Refer to the Sun Microsystems specifications for the JDBC 2.0 Optional Package and the Java Transaction API.

End

End work on behalf of the transaction branch specified by xid, disassociating the transaction branch from its distributed transaction.

void end(Xid xid, int flags)

The flags parameter can have one of the following values:

  • XAResource.TMSUCCESS—This is to indicate that this transaction branch is known to have succeeded.

  • XAResource.TMFAIL—This is to indicate that this transaction branch is known to have failed.

  • XAResource.TMSUSPEND—This is to suspend the transaction branch specified by xid. (By suspending transaction branches, you can have multiple transaction branches in a single session. Only one can be active at any given time, however. Also, this tends to be more expensive in terms of resources than having two sessions.)

TMSUCCESS, TMFAIL, and TMSUSPEND are defined as static members of the XAResource interface and OracleXAResource class.


Notes:

  • Instead of using the end() method with TMSUSPEND, the transaction manager can cast to an OracleXAResource instance and use the suspend(Xid xid) method, an Oracle extension.

  • This XA functionality to suspend a transaction provides a way to switch between various transactions within a single JDBC connection. You can use the XA classes to accomplish this, even if you are not in a distributed transaction environment and would otherwise have no need for the XA classes.

  • If you use TMSUSPEND, you must also use TMNOMIGRATE, as in end(xid, XAResource.TMSUSPEND | OracleXAResource.TMNOMIGRATE);. This prevents the application's receiving the error ORA 1002: fetch out of sequence.

  • In order to avoid Error ORA 1002: fetch out of sequence, include the TMNOMIGRATE flag as part of the end method. For example:

    end(xid, XAResource.TMSUSPEND | OracleXAResource.TMNOMIGRATE);
    

Prepare

Prepare the changes performed in the transaction branch specified by xid. This is the first phase of a two-phase COMMIT operation, to ensure that the database is accessible and that the changes can be committed successfully.

int prepare(Xid xid)

This method returns an integer value as follows:

  • XAResource.XA_RDONLY—This is returned if the transaction branch executes only read-only operations such as SELECT statements.

  • XAResource.XA_OK—This is returned if the transaction branch executes updates that are all prepared without error.

  • n/a (no value returned)—No value is returned if the transaction branch executes updates and any of them encounter errors during preparation. In this case, an XA exception is thrown.

XA_RDONLY and XA_OK are defined as static members of the XAResource interface and OracleXAResource class.


Notes:

  • Always call the end() method on a branch before calling the prepare() method.

  • If there is only one transaction branch in a distributed transaction, then there is no need to call the prepare() method. You can call the XA resource commit() method without preparing first.


Commit

Commit prepared changes in the transaction branch specified by xid. This is the second phase of a two-phase COMMIT and is performed only after all transaction branches have been successfully prepared.

void commit(Xid xid, boolean onePhase)

Set the onePhase parameter as follows:

  • true—This is to use one-phase instead of two-phase protocol in committing the transaction branch. This is appropriate if there is only one transaction branch in the distributed transaction; the prepare step would be skipped.

  • false—This is to use two-phase protocol in committing the transaction branch (typical).

Roll back

Rolls back prepared changes in the transaction branch specified by xid.

void rollback(Xid xid)

Forget

Tells the resource manager to forget about a heuristically completed transaction branch.

public void forget(Xid xid)

Recover

The transaction manager calls this method during recovery to obtain the list of transaction branches that are currently in prepared or heuristically completed states.

public Xid[] recover(int flag)

Note:

Values for flag other than TMSTARTRSCAN, TMENDRSCAN, or TMNOFLAGS, cause an exception to be thrown; otherwise flag is ignored.

The resource manager returns zero or more Xids for the transaction branches that are currently in a prepared or heuristically completed state. If an error occurs during the operation, the resource manager throws the appropriate XAException.

Check for same RM

To determine if two XA resource instances correspond to the same resource manager (database), call the isSameRM() method from one XA resource instance, specifying the other XA resource instance as input. In the following example, presume xares1 and xares2 are OracleXAResource instances:

boolean sameRM = xares1.isSameRM(xares2);

A transaction manager can use this method regarding certain Oracle optimizations, as "Oracle XA Optimizations" explains.

XA ID Interface and Oracle Implementation

The transaction manager creates transaction ID instances and uses them in coordinating the branches of a distributed transaction. Each transaction branch is assigned a unique transaction ID, which includes the following information:

  • format identifier (4 bytes)

    A format identifier specifies a Java transaction manager—for example, there could be a format identifier ORCL. This field cannot be null.

  • global transaction identifier (64 bytes) (or "distributed transaction ID component", as discussed earlier)

  • branch qualifier (64 bytes) (or "transaction branch ID component", as discussed earlier)

The 64-byte global transaction identifier value will be identical in the transaction IDs of all transaction branches belonging to the same distributed transaction. The overall transaction ID, however, is unique for every transaction branch.

An XA transaction ID instance is an instance of a class that implements the standard javax.transaction.xa.Xid interface, which is a Java mapping of the X/Open transaction identifier XID structure.

Oracle implements this interface with the OracleXid class in the oracle.jdbc.xa package. OracleXid instances are employed only in a transaction manager, transparent to application programs or an application server.


Note:

Oracle does not require the use of OracleXid for Oracle XA resource calls. Instead, use any class that implements the javax.transaction.xa.Xid interface.

A transaction manager may use the following in creating an OracleXid instance:

public OracleXid(int fId, byte gId[], byte bId[]) throws XAException

Where fId is an integer value for the format identifier, gId[] is a byte array for the global transaction identifier, and bId[] is a byte array for the branch qualifier.

The Xid interface specifies the following getter methods:

  • public int getFormatId()

  • public byte[] getGlobalTransactionId()

  • public type[] getBranchQualifier()

Error Handling and Optimizations

This section has two focuses: 1) the functionality of XA exceptions and error handling; and 2) Oracle optimizations in its XA implementation. The following topics are covered:

The exception and error-handling discussion includes the standard XA exception class and the Oracle-specific XA exception class, as well as particular XA error codes and error-handling techniques.

XA Exception Classes and Methods

XA methods throw XA exceptions, as opposed to general exceptions or SQLExceptions. An XA exception is an instance of the standard class javax.transaction.xa.XAException or a subclass. Oracle subclasses XAException with the oracle.jdbc.xa.OracleXAException class.

An OracleXAException instance consists of an Oracle error portion and an XA error portion and is constructed as follows by the Oracle JDBC driver:

public OracleXAException()

or:

public OracleXAException(int error)

The error value is an error code that combines an Oracle SQL error value and an XA error value. (The JDBC driver determines exactly how to combine the Oracle and XA error values.)

The OracleXAException class has the following methods:

  • public int getOracleError()

    This method returns the Oracle SQL error code pertaining to the exception—a standard ORA error number (or 0 if there is no Oracle SQL error).

  • public int getXAError()

    This method returns the XA error code pertaining to the exception. XA error values are defined in the javax.transaction.xa.XAException class; refer to its Javadoc at the Sun Microsystems Web site for more information.

Mapping between Oracle Errors and XA Errors

Oracle errors correspond to XA errors in OracleXAException instances as documented in Table 9-2.

Table 9-2 Oracle-XA Error Mapping

Oracle Error Code XA Error Code
ORA 3113 XAException.XAER_RMFAIL
ORA 3114 XAException.XAER_RMFAIL
ORA 24756 XAException.XAER_NOTA
ORA 24764 XAException.XA_HEURCOM
ORA 24765 XAException.XA_HEURRB
ORA 24766 XAException.XA_HEURMIX
ORA 24767 XAException.XA_RDONLY
ORA 25351 XAException.XA_RETRY
all other ORA errors XAException.XAER_RMERR

XA Error Handling

The following example uses the OracleXAException class to process an XA exception:

try {
   ...
   ...Perform XA operations...
   ...
} catch(OracleXAException oxae) { 
  int oraerr = oxae.getOracleError();
  System.out.println("Error " + oraerr);
} 
  catch(XAException xae)
{...Process generic XA exception...}

In case the XA operations did not throw an Oracle-specific XA exception, the code drops through to process a generic XA exception.

Oracle XA Optimizations

Oracle JDBC has functionality to improve performance if two or more branches of a distributed transaction use the same database instance—meaning that the XA resource instances associated with these branches are associated with the same resource manager.

In such a circumstance, the prepare() method of only one of these XA resource instances will return XA_OK (or failure); the rest will return XA_RDONLY, even if updates are made. This allows the transaction manager to implicitly join all the transaction branches and commit (or roll back, if failure) the joined transaction through the XA resource instance that returned XA_OK (or failure).

The transaction manager can use the OracleXAResource class isSameRM() method to determine if two XA resource instances are using the same resource manager. This way it can interpret the meaning of XA_RDONLY return values.

Implementing a Distributed Transaction

This section provides an example of how to implement a distributed transaction using Oracle XA functionality.

Summary of Imports for Oracle XA

You must import the following for Oracle XA functionality:

import oracle.jdbc.xa.OracleXid;
import oracle.jdbc.xa.OracleXAException;
import oracle.jdbc.pool.*;
import oracle.jdbc.xa.client.*;
import javax.transaction.xa.*;

The oracle.jdbc.pool package has classes for connection pooling functionality, some of which are subclassed by XA-related classes.

In addition, if the code will run inside an Oracle database and access that database for SQL operations, you must import the following:

import oracle.jdbc.xa.server.*;

(And if you intend to access only the database in which the code runs, you would not need the oracle.jdbc.xa.client classes.)

The client and server packages each have versions of the OracleXADataSource, OracleXAConnection, and OracleXAResource classes. Abstract versions of these three classes are in the top-level oracle.jdbc.xa package.

Oracle XA Code Sample

This example uses a two-phase distributed transaction with two transaction branches, each to a separate database.

Note that for simplicity, this example combines code that would typically be in a middle tier with code that would typically be in a transaction manager (such as the XA resource method invocations and the creation of transaction IDs).

For brevity, the specifics of creating transaction IDs (in the createID() method) and performing SQL operations (in the doSomeWork1() and doSomeWork2() methods) are not shown here. The complete example is shipped with the product.

This example executes the following sequence:

  1. Start transaction branch #1.

  2. Start transaction branch #2.

  3. Execute DML operations on branch #1.

  4. Execute DML operations on branch #2.

  5. End transaction branch #1.

  6. End transaction branch #2.

  7. Prepare branch #1.

  8. Prepare branch #2.

  9. Commit branch #1.

  10. Commit branch #2.

// You need to import the java.sql package to use JDBC
import java.sql.*;
import javax.sql.*;
import oracle.jdbc.*;
import oracle.jdbc.pool.*;
import oracle.jdbc.xa.OracleXid;
import oracle.jdbc.xa.OracleXAException;
import oracle.jdbc.xa.client.*;
import javax.transaction.xa.*;

class XA4
{
  public static void main (String args [])
       throws SQLException 
  {

    try
    {
        String URL1 = "jdbc:oracle:oci:@";
        // You can put a database name after the @ sign in the connection URL.
        String URL2 ="jdbc:oracle:thin:@(description=(address=(host=dlsun991)
                     (protocol=tcp)(port=5521))(connect_data=(sid=rdbms2)))";
        // Create first DataSource and get connection
        OracleDataSource ods1 = new OracleDataSource();
        ods1.setURL(URL1);
        ods1.setUser("scott");
        ods1.setPassword("tiger");
        Connection conna = ods1.getConnection();

        // Create second DataSource and get connection
        OracleDataSource ods2 = new OracleDataSource();
        ods2.setURL(URL2);
        ods2.setUser("scott");
        ods2.setPassword("tiger");
        Connection connb = ods2.getConnection();

        // Prepare a statement to create the table
        Statement stmta = conna.createStatement ();

        // Prepare a statement to create the table
        Statement stmtb = connb.createStatement ();

        try
        {
          // Drop the test table
          stmta.execute ("drop table my_table");
        }
        catch (SQLException e)
        {
          // Ignore an error here
        }

        try
        {   
          // Create a test table
          stmta.execute ("create table my_table (col1 int)");
        }
        catch (SQLException e)
        {
          // Ignore an error here too
        }

        try
        {
          // Drop the test table
          stmtb.execute ("drop table my_tab");
        }
        catch (SQLException e)
        {
          // Ignore an error here
        }

        try
        {   
          // Create a test table
          stmtb.execute ("create table my_tab (col1 char(30))");
        }
        catch (SQLException e)
        {
          // Ignore an error here too
        }

        // Create XADataSource instances and set properties.
        OracleXADataSource oxds1 = new OracleXADataSource();
        oxds1.setURL("jdbc:oracle:oci:@");
        oxds1.setUser("scott");
        oxds1.setPassword("tiger");

        OracleXADataSource oxds2 = new OracleXADataSource();

        oxds2.setURL("jdbc:oracle:thin:@(description=(address=(host=dlsun991)
                   (protocol=tcp)(port=5521))(connect_data=(sid=rdbms2)))");
        oxds2.setUser("scott");
        oxds2.setPassword("tiger");
    
        // Get XA connections to the underlying datasources
        XAConnection pc1  = oxds1.getXAConnection();
        XAConnection pc2  = oxds2.getXAConnection();

        // Get the physical connections
        Connection conn1 = pc1.getConnection();
        Connection conn2 = pc2.getConnection();

        // Get the XA resources
        XAResource oxar1 = pc1.getXAResource();
        XAResource oxar2 = pc2.getXAResource();

        // Create the Xids With the Same Global Ids
        Xid xid1 = createXid(1);
        Xid xid2 = createXid(2);

        // Start the Resources
        oxar1.start (xid1, XAResource.TMNOFLAGS);
        oxar2.start (xid2, XAResource.TMNOFLAGS);

        // Execute SQL operations with conn1 and conn2
        doSomeWork1 (conn1);
        doSomeWork2 (conn2);

        // END both the branches -- IMPORTANT
        oxar1.end(xid1, XAResource.TMSUCCESS);
        oxar2.end(xid2, XAResource.TMSUCCESS);

        // Prepare the RMs
        int prp1 =  oxar1.prepare (xid1);
        int prp2 =  oxar2.prepare (xid2);

        System.out.println("Return value of prepare 1 is " + prp1);
        System.out.println("Return value of prepare 2 is " + prp2);

        boolean do_commit = true;

        if (!((prp1 == XAResource.XA_OK) || (prp1 == XAResource.XA_RDONLY)))
           do_commit = false;

        if (!((prp2 == XAResource.XA_OK) || (prp2 == XAResource.XA_RDONLY)))
           do_commit = false;

       System.out.println("do_commit is " + do_commit);
        System.out.println("Is oxar1 same as oxar2 ? " + oxar1.isSameRM(oxar2));

        if (prp1 == XAResource.XA_OK)
          if (do_commit)
             oxar1.commit (xid1, false);
          else
             oxar1.rollback (xid1);

        if (prp2 == XAResource.XA_OK)
          if (do_commit)
             oxar2.commit (xid2, false);
          else
             oxar2.rollback (xid2);

         // Close connections
        conn1.close();
        conn1 = null;
        conn2.close();
        conn2 = null;

        pc1.close();
        pc1 = null;
        pc2.close();
        pc2 = null;

        ResultSet rset = stmta.executeQuery ("select col1 from my_table");
        while (rset.next())
          System.out.println("Col1 is " + rset.getInt(1));
  
        rset.close();
        rset = null;

        rset = stmtb.executeQuery ("select col1 from my_tab");
        while (rset.next())
          System.out.println("Col1 is " + rset.getString(1));
  
        rset.close();
        rset = null;

        stmta.close();
        stmta = null;
        stmtb.close();
        stmtb = null;

        conna.close();
        conna = null;
        connb.close();
        connb = null;

    } catch (SQLException sqe)
    {
      sqe.printStackTrace();
    } catch (XAException xae)
    {
      if (xae instanceof OracleXAException) {
        System.out.println("XA Error is " +
                      ((OracleXAException)xae).getXAError());
        System.out.println("SQL Error is " +
                      ((OracleXAException)xae).getOracleError());
      }
    }
  }

  static Xid createXid(int bids)
    throws XAException
  {...Create transaction IDs...}

  private static void doSomeWork1 (Connection conn)
   throws SQLException
  {...Execute SQL operations...}

  private static void doSomeWork2 (Connection conn)
   throws SQLException
  {...Execute SQL operations...}
}