Oracle9i Enterprise JavaBeans Developer's Guide and Reference
Release 1 (9.0.1)

Part Number A90188-01
Go To Documentation Library
Home
Go To Product List
Book List
Go To Table Of Contents
Contents
Go To Index
Index

Master Index

Feedback

Go to previous page Go to next page

7
Transaction Handling

In Oracle9i, Enterprise JavaBeans use Java Transaction API (JTA) 1.0.1 for managing transactions. JTA provides the ability for both bean-managed and container-managed transactions:

This chapter assumes that you have a working knowledge of JTA. The discussion focuses mostly on examples and explaining the differences between the Sun Microsystems JTA specification and the Oracle JTA implementation. See http://www.javasoft.com for the Sun Microsystems JTA specification.

Note that this chapter only covers global transactions involving EJBs. For pure JDBC clients and CORBA applications involved in JTA, see the appropriate CORBA and JDBC manuals.

Transaction Overview

Transactions manage changes to multiple databases within a single application as a unit of work. That is, if you have an application that manages data within one or more databases, you can ensure that all changes in all databases are committed at the same time if they are managed within a transaction.

Transactions are described in terms of ACID properties, which are as follows:

The JTA implementation, specified by Sun Microsystems, relies heavily on the JDBC 2.0 specification and XA architecture. The result is a complex requirement on applications in order to ensure that the transaction is managed completely across all databases. Sun Microsystems's specifies Java Transaction API (JTA) 1.0.1 and JDBC 2.0 on http://www.javasoft.com.

You should be aware of the following when using JTA within the Oracle9i environment:

Global and Local Transactions

Whenever your application connected to a database using JDBC or a SQL server, you were creating a transaction. However, the transaction involved only the single database and all updates made to the database were committed at the end of these changes. This is referred to as a local transaction.

A global transaction involves a complicated set of management objects--objects that track all of the objects and databases involved in the transaction. These global transaction objects--TransactionManager and Transaction--track all objects and resources involved in the global transaction. At the end of the transaction, the TransactionManager and Transaction objects ensure that all database changes are atomically committed at the same time.

Within a global transaction, you cannot execute a local transaction. If you try, the following error will be thrown:

ORA-2089 "COMMIT is not allowed in a subordinate session."

Some SQL commands implicitly execute a local transaction. All SQL DDL statements, such as "CREATE TABLE", implicitly starts and commits a local transaction under the covers. If you are involved in a global transaction that has enlisted the database that the DDL statement is executing against, the global transaction will fail.

Demarcating Transactions

A transaction is said to be demarcated, which means that each transaction has a definite start and stop point. For example, in a client-side demarcated transaction, the client starts the transaction with a begin method and completes the transaction with either executing the commit or rollback method.

The originating client or object that starts the transaction must also end the transaction with a commit or rollback. If the client begins the transaction, calls out to a server object, the client must end the transaction after the invoked method returns. The invoked server object cannot end the transaction.

In a distributed object application, transactions are demarcated differently if the originator is the client or the server. Where the transaction originates defines the transaction as client-side demarcated or server-side demarcated.

UserTransaction Interface

The following are the methods that you can use for client-side or programmatic server-side transaction demarcation. These methods are defined within the javax.transaction.UserTransaction interface:

public abstract void begin() throws NotSupported, SystemException;

public abstract void commit() throws RollbackException, HeuristicMixedException, HeuristicRollbackException, SecurityException, IllegalStateException, SystemException;

public abstract void rollback() throws IllegalStateException, SecurityException, SystemException;

public abstract int getStatus() throws SystemException;

public abstract void setRollbackOnly() throws IllegalStateException, SystemException;

public abstract setTransactionTimeout(int seconds) throws SystemException;

Container-Managed Transactions

The transaction is managed automatically by the container, which begins and ends the transaction depending on the configuration for the bean within the deployment descriptor. All transactional behavior is executed by the container based on the transactional attribute specified in the EJB deployment descriptor. The following table briefly describes the transaction attribute types that should be specified in the bean's deployment descriptor:

Table 7-1 Transaction Attributes

Transaction Attribute  Description 

NotSupported 

The bean is not involved in a transaction. If the bean invoker calls the bean while involved in a transaction, the invoker's transaction is suspended, the bean executes, and when the bean returns, the invoker's transaction is resumed. 

Required 

The bean must be involved in a transaction. If the invoker is involved in a transaction, the bean uses the invoker's transaction. If the invoker is not involved in a transaction, the container starts a new transaction for the bean. 

Supports 

Whatever transactional state that the invoker is involved in is used for the bean. If the invoker has begun a transaction, the invoker's transaction context is used by the bean. If the invoker is not involved in a transaction, neither is the bean. 

RequiresNew 

Whether the invoker is involved in a transaction or not, this bean starts a new transaction that exists only for itself. If the invoker calls while involved in a transaction, the invoker's transaction is suspended until the bean completes. 

Mandatory 

The invoker must be involved in a transaction before invoking this bean. The bean uses the invoker's transaction context. 

Never 

The bean is not involved in a transaction. Furthermore, the invoker cannot be involved in a transaction when calling the bean. If the invoker is involved in a transaction, a RemoteException is thrown. 

See the following for more information:

Bean-Managed Transactions

The bean methods control the transaction demarcation with the UserTransaction object, similar to client-side demarcation. The difference is that the bean retrieves the UserTransaction object through either an in-session JNDI lookup or retrieves it from the SessionContext object.

If the bean specifies itself as bean-managed transactional, it has the following responsibility:

Transaction Context Propagation

When you begin a transaction within either a client or a server instance, JTA denotes the originator in the transaction manager. As the transaction involves more objects and resources, the transaction manager tracks all of these objects and resources in the transaction and manages the transaction for these entities.

When an object calls another object, in order for the invoked object to be included in the transaction, JTA propagates the transaction context to the invoked object. Propagation of the transaction context is necessary for including the invoked object into the global transaction. However, the invoked object must be configured to support transactions and accept this propagation for it to be included in the transaction.

As shown in Figure 7-1, if the client begins a global transaction, calls a server object in the database, the transaction context is propagated to the server object. If the server object supports transactions, this object is attached to the transaction manager as involved in the global transaction. If this server object invokes another server object, within the same or a remote database, the transaction context is propagated to this object as well. This ensures that all objects that are supposed to be involved in the global transaction are tracked by the transaction manager.

Figure 7-1 Connection to an Object over IIOP


Text description of transa.gif follows
Text description of the illustration transa.gif

Propagating the Transactional Context to Bean-Managed Transactional Beans

A bean-managed transactional bean can only initiate transactions, it cannot become involved in an existing transaction. This means that it cannot accept a transactional context from any other object--client or server.

Propagating the Transactional Context to Container-Managed Transactional Beans

The definition of the transaction attribute within the EJB deployment descriptor for container-managed transactional beans determines how the global transaction context is propagated to the server object. You must define one of these attributes for the transaction for a container-managed transactional bean. The following table lists each transaction attribute and the behavior that occurs for each server object type and for any resources.

Table 7-2 Effect of Transactional Attributes for Container-Managed Transactional Beans
Deployment Transaction Attribute  Client Transaction Demarcation  Behavior for Target Server Object or Resource (Database) 

NotSupported 

Does not start transaction 

No transaction started 

Starts transaction 

Invoker's transaction is suspended while the bean executes, resumed when control returns to the invoker. 

Required 

Does not start transaction 

A new transaction is started 

Starts transaction 

Invoker's transaction context is propagated. Server object and the local resource joins the transaction. 

Supports 

Does not start transaction 

No transaction started 

Starts transaction 

Invoker's transaction context is propagated. Server object and the local resource joins the transaction. 

RequiresNew 

Does not start transaction 

A new transaction is started 

Starts transaction 

Invoker's transaction is suspended. A new transaction is started and committed before returning to the invoker. The invoker's transaction is resumed when control is returned to it. 

Mandatory 

Does not start transaction 

Error is returned. This object requires a transactional context. 

Starts transaction 

Invoker's transaction context is propagated. Server object and the local resource joins the transaction. 

Never 

Does not start transaction 

No transaction is started 

Starts transaction 

An error is returned. This object cannot be called from any object--client or server--that is involved in a transaction. 

Enlisting Resources

Each resource, such as an Oracle database, that you want managed in the global transaction must be enlisted in that transaction. The transaction manager tracks all resources involved in the global transaction. Database resources can be included in the global transaction in one of two ways:

Default Enlistment

When the bean is deployed to the database, it can contain an element called <default-enlist>. If this element is set to TRUE (default is FALSE), then when the bean is invoked, the database that this bean resides in will be included in the global transaction. If FALSE, then the only way that the database local to the bean can be included in the transaction is through explicit enlistment--which is described in the next section.

Set the <default-enlist> element in the Oracle-specific deployment descriptor. This element specifics whether the local resource is automatically enlisted in the global transaction or not.

Notice that if you ask for default enlistment of the local database and you include any other database in the transaction, a two-phase commit scenario applies. See "Configuring Two-Phase Commit Engine" for more information.

Explicitly Enlisting the Database

While there are several methods for retrieving a JDBC connection to a database, only one of these methods causes the database to be included in a JTA transaction. The following table lists the normal methods for retrieving JDBC connections:

Table 7-3 JDBC Methods
Retrieval Method  Description 

OracleDriver().
defaultConnection()
 

Pre-JDBC 2.0 method for retrieving the local connection. Use only within local transactions. 

DriverManager.getConnection
("jdbc:oracle:kprb:")
 

Pre-JDBC 2.0 method for retrieving the local connection. Use only within local transactions. 

DataSource.getConnection
("jdbc:oracle:kprb:")
 

JDBC 2.0 method for retrieving connections to the local databases. Can be used for JTA transactions. 

Of these methods, only the DataSource object can be used to include a database resource in the global transaction. In order to ensure that the statements are included within a global transaction, you must do the following:

  1. Bind a JTA DataSource object (OracleJTADataSource) in the JNDI namespace. There are several types of DataSource objects that you can bind. You must bind the JTA type in order for this database to be included in the global transaction.

  2. The bean method must retrieve the DataSource object from the JNDI namespace after the global transaction has started.

  3. Retrieve the connection object from this DataSource object using the getConnection method.


    Note:

    Any JDBC Thin connection originating from an Oracle9i database or Oracle9i Application Server data cache must have been granted the SocketPermission permission. 


An example is shown in "Enlisting Resources on the Server-side".

If your transaction involves more than one database, you must specify an Oracle9i database as the two-phase commit engine. See "Configuring Two-Phase Commit Engine" for more information.

Two-Phase Commit

One of the primary advantages for a global transaction is the number of objects and database resources managed as a single unit within the transaction. If your global transaction involves more than one database resource, you must specify a two-phase commit engine, which is an Oracle9i database designated to manage the changes to all databases within the transaction. The two-phase commit engine is responsible for ensuring that when the transaction ends, all changes to all databases are either totally committed or fully rolled back.

On the other hand, if your global transaction has multiple server objects, but only a single database resource, you do not need to specify a two-phase commit engine. The two-phase commit engine is required only to synchronize the changes for multiple databases. If you have only a single database, single-phase commit can be performed by the transaction manager.


Note:

Your two-phase commit engine can be any Oracle9i database. It can be the database where your server object exists, or even a database that is not involved in the transaction at all. See "Configuring Two-Phase Commit Engine" for a full explanation of the two-phase commit engine setup. 


Figure 7-2 shows three databases enlisted in a global transaction and another database that is designated as the two-phase commit engine. All databases are enlisted when JDBC connections are retrieved from JTA DataSource objects. See "Enlisting Resources" for more information on database enlistment.

When the global transaction ends, the two-phase commit engine ensures that all changes made to the databases A, B, and the local are committed or rolled back simultaneously.

Figure 7-2 Two-Phase Commit for Global Transactions


Text description of jta_2pc.gif follows
Text description of the illustration jta_2pc.gif

JTA Summary

The following sections summarize the details for demarcating the transaction and enlisting the database in the transaction. These details are explained and demonstrated in the rest of the chapter. However, these tables provide a reference point for you.

Environment Initialization

Before you can retrieve the UserTransaction or DataSource bound objects from the JNDI namespace, you must provide the following before the JNDI lookup:

Methods for Enlisting Database Resources

There are two basic methods for enlisting the database, as shown in Table 7-5.

Table 7-5 JDBC Methods Used For Enlisting Databases
Enlisting Method  Description 

<default-enlist> set to TRUE within the bean's deployment descriptor. 

Causes enlistment of the database that is local to where the bean resides. Enlistment occurs after the bean is invoked. Only use when this database is the only database in the transaction. 

DataSource.getConnection
("jdbc:oracle:kprb:")
 

JDBC 2.0 method for retrieving connections to the local databases. If DataSource is bound within the JNDI namespace as a JTA DataSource, and the connection is retrieved after the transaction begins, then this database is enlisted in the transaction. 

The DataSource object is used to explicitly enlist the database in the JTA transaction. In order for the database to be correctly enlisted, the DataSource must be bound correctly, and the retrieval mechanism can be one of three methods. These are discussed below:

Table 7-6 JDBC 2.0 DataSource Overview 
  JDBC 2.0 DataSource 

Binding  

You must bind a JTA DataSource into the namespace with the bindds command. The bindds command must contain the -dstype jta option. 

Retrieving DataSource object from remote JNDI provider 

  1. Provide the environment Hashtable, which contains authentication information and namespace URL.

  2. Retrieve the DataSource object through a JNDI lookup that contains the "jdbc_access://" prefix.

 

Retrieving DataSource object from local JNDI provider 

Retrieve the DataSource object using in-session activation. Environment setup and "jdbc_access://" prefix is not required. 

Retrieve DataSource object that is bound into JNDI provider and is referenced within the environment section of the deployment descriptor. 

Lookup a database through an environment variable that was previously specified in the deployment descriptor. This uses the "java:comp/env" prefix.

Provide username and password if the JNDI provider is remote to this bean. 

Summary of Single-Phase and Two-Phase Commit

Table 7-7 summarizes the single-phase commit scenario. It covers the JNDI binding requirements and the application implementation runtime requirements.

Table 7-7 Single-Phase Commit
Aspect  Description 

Binding  

  • No binding required for UserTransaction. The UserTransaction object is created for you.

  • If using a DataSource object in the transaction, bind it using the bindds command. If the only database involved in the transaction is the local database, use the <default-enlist> element. You do not need any database links.

 

Runtime 

  • Retrieve the UserTransaction through either the EJB 1.0 getUserTransaction method of SessionCtx, the EJB 1.1 method of a JNDI lookup with the "java:comp/
    UserTransaction
    " string, or a normal JNDI lookup.

  • Your runtime is responsible for starting and terminating the transaction.

  • If using the DataSource object to manage SQL DML statements within the transaction, retrieve the DataSource.

 

Table 7-8 summarizes the two-phase commit scenario.

Table 7-8 Two-Phase Commit Requirements  
Aspect  Requirements 

Binding UserTransaction

One of two scenarios: 

Scenario one is where you bind the UserTransaction WITH a username and password that is to be used to complete all global transactions started from this UserTransaction.

  • You bind a UserTransaction object with the fully-qualified database address of the two-phase commit engine and its username and password.

  • You bind DataSource objects for each database involved in the transaction with a fully-qualified public database link from the two-phase commit engine to itself.

 

Scenario two is where you bind the UserTransaction WITHOUT a username and password. Thus, the username that is used when retrieving the UserTransaction is the user that completes the transaction.

  • You bind a UserTransaction object with the fully-qualified database address of the two-phase commit engine.

  • You bind DataSource objects for each database involved in the transaction with a fully-qualified public database link from the two-phase commit engine to itself.

 

Binding DataSource 

You must bind a JTA DataSource for each database involved in the transaction. You must create public database links, as discussed in the System Administration section. 

System Administration 

  • The user that completes the transaction (as described in the binding section) must have the privilege to commit the transaction on all included databases. There are one of two methods for ensuring that the user can complete the transaction.

    - If the username is not bound with the UserTransaction object, the user that retrieves the UserTransaction both starts and stops the transaction. Thus, this user must be created on all involved database in order to be able to open a session to all databases.

    - If the username is bound with the UserTransaction object is different than the user that retrieves the UserTransaction object, the username bound with the UserTransaction object must be given explicit privilege to complete a transaction it did not start. Thus, make sure that this user exists on each database in order to open sessions to all databases and grant it the "CONNECT, REMOVE, CREATE SESSION, and FORCE ANY TRANSACTION" privileges on each database.

  • Create public database links from the two-phase commit engine to each database involved.

 

Runtime 

Runtime requirements are the same as indicated in the single-phase commit table. 

Differences Between Container and Bean-Managed Transactions

Table 7-9 encapsulates the differences between container and bean-managed transactions for the single-phase commit scenario. It covers the differences within the deployment descriptors, the JNDI binding requirements, and the application implementation runtime requirements.

Table 7-9 Differences Between Container and Bean-Managed Transactions for Single-Phase Commit
Aspect
 
Container-Managed Transaction
Allowed for entity and session beans.
 
Bean-Managed Transaction
Allowed only for session beans.
 

Deployment Descriptor 

  • Define that this is container-managed in the <transaction-type> element.

  • Define the <trans-attribute> element--one of the values described in Table 7-2--within the <container-transaction> element in the XML deployment descriptor.

 
  • Define that this is bean-managed in the <transaction-type> element.

 

Binding  

  • No binding required for UserTransaction. The UserTransaction object is created for you.

  • If using a DataSource object in the transaction, bind it using the bindds command. If the only database involved in the transaction is the local database, use the <default-enlist> element. You do not need any database links. See Table 7-6 for more information.

 
  • No binding required for UserTransaction. The UserTransaction object is created for you.

  • If using a DataSource object in the transaction, bind it using the bindds command. If the only database involved in the transaction is the local database, use the <default-enlist> element. You do not need any database links.

 

Runtime 

  • Cannot retrieve the UserTransaction. The container manages the UserTransaction for you.

  • If using the DataSource object to manage SQL DML statements within the transaction, retrieve the DataSource, execute the getConnection method, and issue SQL based off of the Connection object.

 
  • Retrieve the UserTransaction either through the EJB 1.0 getUserTransaction method of SessionCtx, the EJB 1.1 method of a JNDI lookup with the "java:comp/
    UserTransaction
    " string, or a normal JNDI lookup.

  • Your runtime is responsible for starting and terminating the transaction.

  • If using the DataSource object to manage SQL DML statements within the transaction, retrieve the DataSource.

 

For the two-phase commit scenario, both bean-managed and container-managed transactions include the same requirements as designated in the single-phase scenario (shown in Table 7-9). In addition, both types of transactions must also do the extra requirements designated in Table 7-10.

Table 7-10 Two-Phase Commit Requirements  
Aspect  Additional Requirements 

Deployment Descriptor 

In addition to the single-phase requirements, you must also add the JNDI bound name for the UserTransaction object in the <transaction-manager> element in the Oracle-specific deployment descriptor. 

Binding UserTransaction

One of two scenarios: 

Scenario #1 is where you bind the UserTransaction WITH a username and password that is to be used to complete all global transactions started from this UserTransaction.

  • You bind a UserTransaction object with the fully-qualified database address of the two-phase commit engine and its username and password.

  • You bind DataSource objects for each database involved in the transaction, including the local database, with a fully-qualified public database link from the two-phase commit engine to itself.

 

Scenario #2 is where you bind the UserTransaction WITHOUT a username and password. Thus, the username that is used when retrieving the UserTransaction is the user that completes the transaction.

  • You bind a UserTransaction object with the fully-qualified database address of the two-phase commit engine.

  • You bind DataSource objects for each database involved in the transaction with a fully-qualified public database link from the two-phase commit engine to itself.

 

Binding DataSource 

You must bind a JTA DataSource for each database involved in the transaction. You must create public database links, as discussed in the System Administration section. 

System Administration 

  • The user that completes the transaction (as described in the binding section) must have the privilege to commit the transaction on all included databases. There are one of two methods for ensuring that the user can complete the transaction.

    - If the username is not bound with the UserTransaction object, the user that retrieves the UserTransaction both starts and stops the transaction. Thus, this user must be created on all involved database in order to be able to open a session to all databases.

    - If the username is bound with the UserTransaction object is different than the user that retrieves the UserTransaction object, the username bound with the UserTransaction object must be given explicit privilege to complete a transaction it did not start. Thus, make sure that this user exists on each database in order to open sessions to all databases and grant it the "CONNECT, REMOVE, CREATE SESSION, and FORCE ANY TRANSACTION" privileges on each database.

  • Create public database links from the two-phase commit engine to each database involved.

 

Runtime 

Runtime requirements are the same as indicated in the single-phase commit table. 

JTA Server-Side Demarcation

Server-side demarcation can be performed either through a container-managed or bean-managed transactions. A container-managed transaction is specified within the EJB deployment descriptor for the bean. Most EJBs use container-managed transactions as it requires no transaction logic within the application.

No matter which method that the server object decides to demarcate transactions, the enlistment of the database is the same for both container and bean-managed transactions.

Container-Managed Transactions

You can declare that the container manages the transaction for the bean within the EJB deployment descriptor. Based upon the transaction attribute you specify within the EJB deployment descriptor, the container will begin, commit, or rollback global transactions when a method in the bean instance is invoked. Each bean can be specified with different transaction attributes. The transaction attributes specify how the transaction demarcation is handled. This means that your bean does not retrieve the UserTransaction object, nor invokes any of its methods. The container does this for you. To enable container-managed transactions within your session or entity bean, do the following:

  1. Specify the container-managed transaction elements within the deployment descriptor. See "Defining Transactions" for full instructions.

    1. "Container" within the <transaction-type> element in the deployment descriptor.

    2. Specify the container-managed transaction attribute (see Table 7-2) within the <container-transaction> element in the deployment descriptor.

  2. Enlist the database resources involved in the global transaction. You can enlist a database in one of two ways:

    • Set the <default-enlist> element to TRUE--If you set the <default-enlist> element to TRUE within the Oracle-specific deployment descriptor, then the database where the bean resides is automatically enlisted when the bean is included in the transaction. This should only be used if the local database is the only database involved in the transaction.

      Normally, you want the database that is local to the EJB to be enlisted. However, if the EJB is deployed to the Oracle9i Application Server, you do not want the local resource, the Oracle Database Cache, to be enlisted.

      • Specify TRUE if the EJB is deployed to an Oracle9i database.

      • Specify FALSE if the EJB is deployed to an Oracle9i Application Server.

      The default value for this element is FALSE.

    • Explicitly enlist the database by retrieving a JDBC connection through a JTA DataSource object--You must retrieve a previously bound JTA DataSource object for the database within the context of a transaction. Retrieving the JDBC connection to this object enlists the database in the transaction.

      • Before executing your application, bind each DataSource that represents your database within the namespace with the bindds command. Remember that all DataSource objects must be bound with the -dstype jta for inclusion in the transaction.

      • Within your application, enlist each database by retrieving the database connection through the getConnection method of the DataSource object.

      Optionally, for easier retrieval within your code, you can specify bound DataSource objects within the <resource-ref> elements in the deployment descriptor.

Example 7-1 Bind the DataSource Object in the Namespace

To bind a DataSource object for a single-phase commit transaction with the empHost database to the name "/test/DataSource/empDS" in the namespace located on nsHost, execute the following:

sess_sh -service jdbc:oracle:thin:@nsHost:5521:ORCL 
-user SCOTT -password TIGER
&bindds /test/DataSource/empDS -url jdbc:oracle:thin:@empHost:5521:ORCL
-dstype jta

After binding the DataSource object in the namespace, the server can retrieve this object from JNDI. If retrieved within a global transaction, the getConnection method causes this database to be enlisted in the transaction. See "Bind DataSource Object in the Namespace" for more information.

Example 7-2 EmployeeBean EJB Deployment Descriptor

The EmployeeBean is specified as a container-managed transaction bean with the RequiresNew attribute. Also, for easy retrieval, the JDBC database resource objects are defined as environment variables.

<?xml version="1.0"?>
<!DOCTYPE ejb-jar PUBLIC "-//Sun Microsystems Inc.//DTD Enterprise JavaBeans 1.1
//EN" "ejb-jar.dtd">
<ejb-jar>
   <enterprise-beans>
      <session>
       ...
         <transaction-type>Container</transaction-type>
         <resource-ref>
           <res-ref-name>jdbc/EmployeeDS</res-ref-name>
           <res-type>javax.sql.DataSource</res-type>
           <res-auth>Application</res-auth>
         </resource-ref>
      </session>
   </enterprise-beans>
   <assembly-descriptor>
     ...
      <container-transaction>
         <method>
            <ejb-name>test/EmployeeBean</ejb-name>
            <method-name>*</method-name>
         </method>
         <trans-attribute>RequiresNew</trans-attribute>
      </container-transaction>
   </assembly-descriptor>
</ejb-jar>

Example 7-3 EmployeeBean Oracle-Specific Deployment Descriptor

The environment variables for the JDBC objects are mapped to the JNDI bound names. This bean is deployed to an Oracle9i database that is the only database involved in this transaction, so the <default-enlist> element is set to TRUE.
<?xml version="1.0"?>
<!DOCTYPE oracle-descriptor PUBLIC "-//Sun Microsystems Inc.//DTD Enterprise Jav
aBeans 1.1//EN" "oracle-ejb-jar.dtd">
<oracle-ejb-jar>
 <oracle-descriptor>
  <mappings>
    ...
    <resource-ref-mapping>
      <res-ref-name>jdbc/EmployeeDS</res-ref-name>
      <jndi-name>test/DataSource/empDS</jndi-name>
    </resource-ref-mapping>
    <transaction-manager>
      <default-enlist>true</default-enlist>
    </transaction-manager>
  </mappings>
 </oracle-descriptor>
</oracle-ejb-jar>

Example 7-4 EmployeeBean Using Container-Managed Transactions

With container-managed transactions, you do not need to retrieve a UserTransaction object to start and stop the transaction. Instead, the container will demarcate the transaction based upon the <trans-attribute> element within the EJB deployment descriptor. For the EmployeeBean example, the <trans-attribute> was set to RequiresNew. The container will start a new transaction when this bean is invoked.

The <default-enlist> element was set to TRUE, so the database where the bean resides is automatically enlisted in the global transaction. You do not need to retrieve its DataSource in order to enlist the database. If you can use SQLJ for updating the local database, you will not need to retrieve a DataSource for the SQL statements to be included in the transaction. However, if you need to use JDBC for updating the local database, you retrieve the JDBC connection in the same manner as for enlisting a remote database--by retrieving a bound JTA DataSource from the JNDI provider and using getConnection to retrieve the JDBC connection to that database.

Since the SQLJ statement alone is not an interesting example, the following shows a container-managed transactional bean that retrieves the JDBC connection to the local database for executing JDBC statements against. The local database is bound in the JNDI namespace with a JTA DataSource that is identified by the EJB environment variable "jdbc/EmployeeDS". From the retrieved DataSource, it retrieves a connection to the remote Oracle9i database. Since the database is local, no username and password is required in the getConnection method. If the database was remote, you would need to provide authentication information. Lastly, remember to close all connections to the database once the statements are completed.

Notice that no UserTransaction object is necessary for demarcating the transaction.

Context ic = new InitialContext ();

//retrieve the DataSource to the local database - this database has already
//been enlisted. Just retrieving a JDBC connection for performing JDBC work.
DataSource empDS = (DataSource)ic.lookup("java:comp/env/jdbc/EmployeeDS");

// get a connection to the local database, no need for username/password
Connection empConn = empDS.getConnection();

//perform JDBC work given the empConn object...
...

//close database connection
empConn.close();

Bean-Managed Transactions

Only session beans have the option to use bean-managed transactions. This means that only session beans can demarcate the transaction with the begin, commit, and rollback methods. Thus, the bean programmatically starts and stops the transaction. All resources are enlisted when you retrieve connections to DataSource objects that have been bound correctly within the JNDI namespace.

In order to specify that this session bean is going to programmatically demarcate its transaction, it must do the following:

  1. Specify "Bean" within the <transaction-type> element in the deployment descriptor. See "Defining Transactions" for full instructions.

  2. Enlist the database resources involved in the global transaction. The enlistment for databases within a bean-managed transaction is the same as with a container-managed transaction. See "Container-Managed Transactions" for an example.

    1. Before executing your application, bind each DataSource that represents your database within the namespace as indicated within Table 7-6. See "Bind DataSource Object in the Namespace" for more information.

    2. Within your application, enlist each database by retrieving the database connection through one of the methods listed in Table 7-3.

      Optionally, for easier retrieval within your code, you can specify bound DataSource objects within the <resource-ref> elements in the deployment descriptor.

    3. Specify <default-enlist> value.

      • Specify TRUE if the EJB is deployed to an Oracle9i database. This can only be used if the local database is the only database involved in the transaction.

      • Specify FALSE if the EJB is deployed to an Oracle9i Application Server.

  3. Use the methods of the UserTransaction interface to programmatically start and stop the transaction. See "UserTransaction Interface" for more information.

Bean-Managed Transactional Deployment Descriptor

For all session beans that are bean-managed transactional, specify the following in the EJB deployment descriptor:

<enterprise-beans>
 <session>
  . . .
  <transaction-type>Bean</transaction-type>
 </session>
</enterprise-beans>

Since this is deployed to an Oracle9i database, set the <default-enlist> element is to TRUE in the Oracle-specific deployment descriptor.
<?xml version="1.0"?>
<!DOCTYPE oracle-descriptor PUBLIC "-//Sun Microsystems Inc.//DTD Enterprise Jav
aBeans 1.1//EN" "oracle-ejb-jar.dtd">
<oracle-ejb-jar>
 <oracle-descriptor>
  <mappings>
    ...
    <transaction-manager>
      <default-enlist>true</default-enlist>
    </transaction-manager>
  </mappings>
 </oracle-descriptor>
</oracle-ejb-jar>


Note:

See "Defining Transactions" for a full description of defining transaction management in the EJB deployment descriptor. 


Enlisting Resources in Bean-Managed Transactions

The session bean implementation demarcates transactions in the same manner as a client would. It invokes the begin, commit, and rollback methods off of the UserTransaction object. The only difference is how the UserTransaction object is retrieved. There are two methods for retrieving the UserTransaction object on the server side in a bean-managed transactional bean:

SessionContext getUserTransaction method

The bean retrieves the UserTransaction object from the SessionContext object, which was set within the setSessionContext method. The container has already retrieved the UserTransaction object for you. The following shows that the session context is saved in the ctx variable.

public void setSessionContext (SessionContext ctx) {
    this.ctx = ctx;
}

Within the bean implementation, retrieve the UserTransaction from the session context and begin the transaction.

UserTransaction ut = ctx.getUserTransaction();
ut.begin(); 
...
ut.commit();
JNDI lookup

The EJB 1.1 method retrieves the UserTransaction object by performing an in-session lookup with the following JNDI name: "java:comp/UserTransaction". In a single-phase commit environment, the container will create the UserTransaction object for you; in a two-phase environment, you must have already bound a UserTransaction object with the two-phase commit information (username, password, and two-phase commit URL) into the namespace. After retrieval, you use this object to demarcate your global transaction. See "Bind UserTransaction Object in the Namespace" for more information.

The following demonstrates retrieving a UserTransaction object from the namespace:

ic = new InitialContext ( );

// lookup the usertransaction
UserTransaction ut = (UserTransaction)ic.lookup ("java:comp/UserTransaction");
ut.begin ();
...
ut.commit();

JTA Client-Side Demarcation

For JTA, client-side demarcated transactions are programmatically demarcated through the UserTransaction interface (see "UserTransaction Interface"). A UserTransaction object must be bound with the bindut command into the namespace (see "Bind UserTransaction Object in the Namespace"). With client-side transaction demarcation, the client controls the transaction. The client starts a global transaction by invoking the UserTransaction begin method; it ends the transaction by invoking either the commit or rollback methods. In addition, the client must always set up an environment including a Hashtable with authentication information and namespace location URL.

Figure 7-3 shows a client invoking a server object--which, in this example, is a container-managed transactional bean. The client starts a global transaction, then invokes the bean. Since the bean is a container-managed transactional bean and is specified with the transactional attribute of "Supports", the transactional context is propagated to include the server object. If we assume that the bean has defined <default-enlist> to be TRUE, then the database is also automatically enlisted in the transaction.

Figure 7-3 Client Demarcated Global Transaction


Text description of jta_cli_.gif follows
Text description of the illustration jta_cli_.gif

The following must occur for the client to demarcate the transaction:

  1. Initialize a Hashtable environment with the namespace address and authentication information.

  2. Retrieve the UserTransaction object from the namespace within the client logic. When you retrieve the UserTransaction object from any client, the URL must consist of "jdbc_access://" prefix before the JNDI name.

  3. Start the global transaction within the client using UserTransaction.begin().

  4. Retrieve the server bean. The container will include this bean in the transaction if the transactional attribute declares that it should either be included or that a new transaction should be started.

  5. Invoke any object methods to be included in the transaction.

  6. End the transaction through UserTransaction.commit() or UserTransaction.rollback().

Example7-5 shows a client that invokes a server bean within the transaction.

Example 7-5 Bind UserTransaction Object in Namespace

Before starting the client, you must first bind the UserTransaction object in the namespace. To bind a UserTransaction object to the name "/test/myUT" in the namespace located on nsHost, execute the following:

sess_sh -service jdbc:oracle:thin:@nsHost:5521:ORCL -user SCOTT -password TIGER
& bindut /test/myUT

See "Bind UserTransaction Object in the Namespace" for more information.

Developing the Client Application

After binding the UserTransaction object, your client code can retrieve the UserTransaction object and start a global transaction. Since the client is retrieving the UserTransaction object from a remote site, the lookup requires authentication information, location of the namespace, and the "jdbc_access://" prefix.

//Set up the service URL to where the UserTransaction object 
//is bound. Since from the client, the connection to the database
//where the namespace is located can be communicated with over either
//a Thin or OCI JDBC driver. This example uses a Thin JDBC driver.
String namespaceURL = "jdbc:oracle:thin:@nsHost:1521:ORCL";
 
//User and password are case sensitive.
String user = "SCOTT";
String password = "TIGER";

//1.(a) Authenticate to the database.
// create InitialContext and initialize for authenticating client
Hashtable env = new Hashtable ();
env.put (Context.URL_PKG_PREFIXES, "oracle.aurora.jndi");
env.put (Context.SECURITY_PRINCIPAL, user);
env.put (Context.SECURITY_CREDENTIALS, password);
env.put (Context.SECURITY_AUTHENTICATION, ServiceCtx.NON_SSL_LOGIN);
//1.(b) Specify the location of the namespace where the transaction objects
//   are bound.
env.put(jdbc_accessURLContextFactory.CONNECTION_URL_PROP, namespaceURL);
Context ic = new InitialContext (env);

//2. Retrieve the UserTransaction object from JNDI namespace
UserTransaction ut = (UserTransaction)ic.lookup ("jdbc_access://test/myUT");

//3. Start the transaction
ut.begin();

//4. Retrieve the EJB 
// get an handle to the employee_home object
EmployeeHome employee_home =
(EmployeeHome)ic.lookup ("sess_iiop://myhost:1521:orcl/test/employee");

// get an handle to the remote bean
Employee employee = employee_home.create ();

//5. Perform bean business logic.
...

//6. End the transaction
//Commit the updated value
ut.commit();

JTA Client-Side Demarcation Including Databases

The previous example showed how a transaction context was propagated to server objects from a client within the JTA global transaction. When you execute the server object, the transaction is propagated over the IIOP transport layer. In addition to invoking IIOP server objects, you may wish to update databases over JDBC connections. This section shows how you enlist databases using a JDBC connection in tandem with the IIOP server object propagation.

Figure 7-4 demonstrates how the client can open both an IIOP and a JDBC connection to the database. To open the JDBC connection within the context of a global transaction, you must use a JTA DataSource object.

Figure 7-4 Client Creating Both JDBC and IIOP Connections


Text description of jta_two.gif follows
Text description of the illustration jta_two.gif

To include a remote database within the transaction from a client, you must use a DataSource object, which has been bound in the namespace as a JTA DataSource. Then, invoke the getConnection method of the DataSource object after the transaction has started, and the database is included in the global transaction. See "Enlisting Resources" for more information.

The following must occur in the client runtime to demarcate the transaction:

  1. Initialize a Hashtable environment with the namespace address and authentication information.

  2. Retrieve the UserTransaction object from the namespace within the client logic. When you retrieve the UserTransaction object from the client, the URL must consist of "jdbc_access://" prefix before the JNDI name.

  3. Start the global transaction within the client using UserTransaction.begin().

  4. Enlist any database resources to be included in the transaction by opening a connection to the specified database, as follows:

    1. Retrieve the DataSource object from the namespace within the client logic. When you retrieve the DataSource object from any client, the URL must consist of "jdbc_access://" prefix before the JNDI name.

    2. Open a connection to the database through DataSource.getConnection method.

  5. Retrieve the bean reference.

  6. Invoke any object methods to be included in the transaction.

  7. Invoke SQL DML statements against any enlisted databases. SQL DDL creates a local transaction that will abort the global transaction. Thus, SQL DDL cannot be executed within a JTA transaction.

  8. End the transaction through UserTransaction.commit() or UserTransaction.rollback().

Example7-6 shows a client that invokes a bean and enlists a single database within the transaction.

Example 7-6 Employee Client Code for Client Demarcated Transaction

Before starting the client, you must first bind the UserTransaction and DataSource objects in the JNDI namespace. See "Bind UserTransaction Object in the Namespace" and "Bind DataSource Object in the Namespace" for directions on the binding these objects.

sess_sh -service jdbc:oracle:thin:@nsHost:5521:ORCL -user SCOTT -password TIGER
> bindut /test/myUT
> bindds /test/DataSource/empDB -url jdbc:oracle:thin:@empHost:5521:ORCL 
-dstype jta

Developing the Client Application

The following example follows the steps listed in "JTA Client-Side Demarcation Including Databases".

//Set up the service URL to where the UserTransaction object 
//is bound. Since from the client, the connection to the database
//where the namespace is located can be communicated with over either
//a Thin or OCI JDBC driver. This example uses a Thin JDBC driver.
String namespaceURL = "jdbc:oracle:thin:@nsHost:1521:ORCL";

//User and password are case sensitive.
String user = "SCOTT";
String password = "TIGER";

//1.(a) Authenticate to the database.
// create InitialContext and initialize for authenticating client
Hashtable env = new Hashtable ();
env.put (Context.URL_PKG_PREFIXES, "oracle.aurora.jndi");
env.put (Context.SECURITY_PRINCIPAL, user);
env.put (Context.SECURITY_CREDENTIALS, password);
env.put (Context.SECURITY_AUTHENTICATION, ServiceCtx.NON_SSL_LOGIN);
//1.(b) Specify the location of the namespace where the transaction objects
//   are bound.
env.put(jdbc_accessURLContextFactory.CONNECTION_URL_PROP, namespaceURL);
Context ic = new InitialContext (env);

//2. Retrieve the UserTransaction object from JNDI namespace
UserTransaction ut;
ut = (UserTransaction)ic.lookup ("jdbc_access://test/myUT");

//3. Start the transaction
ut.begin();

//4.(a) Retrieve the DataSource (that was previously bound with bindds in
// the namespace. After retrieving the DataSource...
// get a connection to a database. You need to provide authentication info
// for a remote database lookup, similar to what you would do from a client.
// In addition, if this was a two-phase commit transaction, you must provide
// the username and password.
DataSource ds = (DataSource)ic.lookup ("jdbc_access://test/empDB");

// 4.(b) Get connection to the database through DataSource.getConnection
// in this case, the database requires the same username and password as
// set in the environment.
Connection conn = ds.getConnection ("SCOTT", "TIGER");

//5. Retrieve the EJB 
// get an handle to the employee_home object
EmployeeHome employee_home =
(EmployeeHome)ic.lookup (serviceURL + objectName);

// get an handle to the remote bean
Employee employee = employee_home.create ();

//6. Perform bean business logic.
...

//7. Close the database connection.
conn.close ();

//8. End the transaction
//Commit the updated value
ut.commit();

Enlisting Resources on the Server-side

Whether your bean instance uses bean-managed or container-managed transactions, the databases that the bean accesses must be enlisted to be included within the global transaction. This is discussed more in "Enlisting Resources" and "Bind DataSource Object in the Namespace".

Once bound, you can enlist the database after the transaction begins.

Local Database Resource Enlistment

The local resource--whether it is an Oracle9i database or an Oracle9i Application Server database cache--is automatically enlisted in the transaction when the <default-enlist> element is set to TRUE. You can only enlist using this element when you are in a single-phase commit scenario. That is, the local resource is the only resource involved in the transaction. If you have other databases involved in this transaction, you can only use the explicit method of enlistment by binding a JTA DataSource object for each database.

The <default-enlist> value describes whether the resource local to the EJB should be automatically enlisted within the transaction or not. That is, the database or database cache resource where the bean resides can be automatically enlisted in the transaction unless specified not to. Normally, you want the database that is local to the EJB to be enlisted. However, if the EJB is deployed to the Oracle9i Application Server, you do not want the local resource, the Oracle Database Cache, to be enlisted.

If the resource is automatically enlisted within the transaction, all SQL commands executed against this database are committed when the transaction is committed.

  • SQLJ

As discussed in the Oracle9i SQLJ Developer's Guide and Reference, an implicit local connection is supplied to the database that the object is running in. Any statements executed within the SQLJ statement is executed against the local database. However, in order for the statement results to be part of the transaction, the <default-enlist> element must be set to TRUE and you must execute the SQLJ statement within an open global transaction. That is, the SQLJ statement must be executed after the UserTransaction.begin method is invoked. However, do not commit the transaction within a SQLJ statement. Commitment of the transaction should only occur within by the UserTransaction.commit method.

  • JDBC

You can create a connection by retrieving the JTA DataSource and executing the DataSource.getConnection("jdbc:oracle:kprb:") method.

Always execute these methods and subsequent SQL statements after the global transaction has started.

Example 7-7 DataSource.getConnection Method Example

The following example is a container-managed transactional bean with RequiresNew attribute, so the global transaction is initialized when the bean is first invoked. The local connection is retrieved through the DataSource.getConnection method, which enlists the database in the transaction. SQL statements are executed against the local database. These statements are committed when the global transaction is committed by the container when the bean exits.

The JTA DataSource must have been bound previously in the JNDI namespace. In this case, the bindds would have been for the local database with the KPRB driver, as follows:

bindds /test/myDB -url jdbc:oracle:kprb: -dstype jta

The EJB deployment descriptor defines the DataSource as an environment variable, as follows:

<ejb-jar>
   <enterprise-beans>
      <session>
         ...
         <resource-ref>
           <res-ref-name>jdbc/EmployeeDS</res-ref-name>
           <res-type>javax.sql.DataSource</res-type>
           <res-auth>Application</res-auth>
         </resource-ref>
     </session>
   </enterprise-beans>

The Oracle-specific descriptor maps "jdbc/EmployeeDS" to its JNDI bound name "/test/myDB".

<oracle-ejb-jar>
 <oracle-descriptor>
  <mappings>
    ...
    <resource-ref-mapping>
      <res-ref-name>jdbc/EmployeeDS</res-ref-name>
      <jndi-name>test/myDB</jndi-name>
    </resource-ref-mapping>
    ...
  </mappings>
 </oracle-descriptor>
</oracle-ejb-jar>

After binding, the bean can retrieve and use this object, as follows:

public EmpRecord query (int empNumber) throws SQLException, RemoteException
  {
   //Retrieving the UserTransaction and DataSource using in-session activation
    Context ic = new InitialContext ( );

    //Retrieve the DataSource using in-session activation
    DataSource ds = (DataSource) ic.lookup("java:comp/env/jdbc/EmployeeDS");

    //Retrieve the local connection object to the local database
    Connection conn = ds.getConnection ();  

    //prepare and execute a sql statement against the local database.
    PreparedStatement ps =
      conn.prepareStatement ("select ename, sal from emp where empno = ?");
    ...//do work
    ps.close();

    //close the database connection
    conn.close();
  }

Using SQLJ After Database Enlistment

You can perform the previous function with SQLJ in the case where the database is enlisted automatically with <default-enlist> or where the database is explicitly enlisted with the DataSource object.

Example 7-8 Using SQLJ with <default-enlist>

Alternatively to Example7-7, you can automatically enlist the local database with the <default-enlist> element and use SQLJ for updating the database.

The following example is a container-managed transactional bean with RequiresNew attribute, so the global transaction is initialized when the bean is first invoked. In addition, the local database is automatically enlisted because the Oracle-specific deployment descriptor defines <default-enlist> to be TRUE.

SQLJ statements are executed against the local database. These statements are committed when the global transaction is committed by the container when the bean exits.

public EmpRecord query (String name) throws SQLException, RemoteException
{
 int empno = 0;
 double salary = 0.0;
 #sql { select empno, sal into :empno, :salary from emp
        where ename = :name };
 System.out.println ("  " + name + " sal = " + salary);
}

Example 7-9 Using SQLJ with Explicit Enlistment

With the same deployment descriptors that are described in Example7-7, you would retrieve the JTA DataSource from the JNDI provider, retrieve the connection, retrieve a context from that connection, and then provide the context on the SQLJ command-line.

public EmpRecord query (int empNumber) throws SQLException, RemoteException
  {
   //Retrieving the UserTransaction and DataSource using in-session activation
    Context ic = new InitialContext ( );

    //Retrieve the DataSource using in-session activation
    DataSource ds = (DataSource) ic.lookup("java:comp/env/jdbc/EmployeeDS");

    //Retrieve the local connection object to the local database
    Connection conn = ds.getConnection ();  

    //setup the context for issuing SQLJ against the database
    DefaultContext defCtx = new DefaultContext (conn);

    //issue SQL DML statements against the database
    #sql [defCtx] { update emp set ename = :(remoteEmployee.name), 
	sal = :(remoteEmployee.salary) 
	  where empno = :(remoteEmployee.number) };

    //close the database connection
    conn.close();
  }

Remote Oracle9i Database Enlistment

If you access a remote Oracle9i database from the server that should be included in the transaction, you must open the connection to the database after the global transaction starts.


Note:

At this time, the Oracle JTA implementation does not support including non-Oracle databases in a global transaction. 


Example 7-10 Enlist Database in Single Phase Transaction

The following example is a container-managed transactional bean, so it does not retrieve the UserTransaction. However, it does retrieve a DataSource to start a JDBC 2.0 connection.

    // get a connection to the local database. If this was a two-phase commit 
    // transaction, you would provide the username and password 
// for the 2pc engine DataSource ds = (DataSource)ic.lookup ("/test/myDB"); // get connection to the local database through DataSource.getConnection Connection conn = ds.getConnection ("SCOTT", "TIGER"); //perform your SQL against the database. //prepare and execute a sql statement.
//retrieve the employee's selected benefits PreparedStatement ps = conn.prepareStatement ("update emp set ename = :(employee.name),
sal = :(employee.salary) where empno = :(employee.number)"); ... //do work... ps.close(); } //close the connection conn.close(); return new EmployeeInfo (name, empno, salary); }

Binding Transactional Objects in the Namespace

For most global transactions, you will need to bind at least one of the following objects in the namespace:

Notice that if you have a container-managed transaction that only includes beans in the transaction--and no database resources--you do not need to bind either one of these objects in the namespace.

Bind UserTransaction Object in the Namespace

The bindut command binds a UserTransaction object in the namespace. This object is used for demarcation of global transactions by either a client or by a session bean that uses bean-demarcated transactions.

You must bind a UserTransaction object for both single and two-phase commit transactions through the bindut command of the sess_sh tool.

The options used to bind a UserTransaction object depend on whether the transaction uses a single or two-phase commit, as described below:

Single-Phase Commit Binding for UserTransaction

Single-phase commit requires the JNDI bound name for the UserTransaction object. You do not need to provide the address to a two-phase commit engine. For example, the following binds a UserTransaction with the name of "/test/myUT" that exists for a single-phase commit transaction:

bindut /test/myUT

To bind a UserTransaction object to the name "/test/myUT" in the namespace located on nsHost through the sess_sh command, execute the following:

sess_sh -service jdbc:oracle:thin:@nsHost:5521:ORCL -user SCOTT -password TIGER
& bindut /test/myUT
Two-Phase Commit Binding for UserTransaction

Two-phase commit binding requires the JNDI bound name for the UserTransaction object and the address to a two-phase commit engine. You provide a URL for the two-phase commit engine in the bindut command, which can be either a JDBC URL or a sess_iiop URL.


Note:

The client needs the same information to retrieve the UserTransaction as you give within the bindut command.  


In addition, you can bind a username and password with the UserTransaction object.

The username that is used to commit or rollback the two-phase commit transaction must be created on the two-phase commit engine and on each database involved in the transaction. It needs to be created so that it can open a session from the two-phase commit engine to each of the involved databases using database links. Secondly, it must be granted the CONNECT, RESOURCE, CREATE SESSION privileges to be able to connect to each of these databases. For example, if the user that is needed for completing the transaction is SCOTT, you would do the following on the two-phase commit engine and each database involved in the transaction:

CONNECT SYSTEM/MANAGER;
CREATE USER SCOTT IDENTIFIED BY SCOTT;
GRANT CONNECT, RESOURCE, CREATE SESSION TO SCOTT;

Lastly, if you bound a username and password with the UserTransaction object, it will be using a different username to finalize the transaction than the username used to start the transaction. For this to be allowed, you must grant the FORCE ANY TRANSACTION privileges on each database involved in the transaction in order for two separate users to start and stop the transaction. If SCOTT is the username bound with the UserTransaction object, you would need to do the following in addition to the previous grant:

GRANT FORCE ANY TRANSACTION TO SCOTT;

The following binds a UserTransaction with the name of "/test/myUT" and a two-phase commit engine at "2pcHost" using a JDBC URL:

bindut /test/myUT -url jdbc:oracle:thin:@2pcHost:5521:ORCL

To bind the UserTransaction in the namespace designating the two-phase commit engine at dbsun.mycompany.com with a sess_iiop URL:

bindut /test/myUT -url sess_iiop://dbsun.mycompany.com:2481:ORCL

When the transaction commits, the UserTransaction communicates with the two-phase engine designated in the -url option to commit all changes to all included databases. In this example, the username and password were not bound with the UserTransaction object, so the user that retrieves the UserTransaction object from the JNDI namespace is used to start and stop the transaction. Thus, this user must exist on all involved databases and the two-phase commit engine. The UserTransaction tracks all databases involved in the transaction; the two-phase commit engine uses the database links for these databases to complete the transaction.


Note:

If you change the two-phase commit engine, you must update all database links on all DataSource objects involved in the transaction, and rebind the UserTransaction


Bind DataSource Object in the Namespace

The bindds command binds a DataSource object in the JNDI namespace. In order to enlist any database in a global transaction--including the local database--you must bind a JTA DataSource object to identify each database included in the transaction. There are multiple types of DataSource objects for use with certain scenarios. However, for use with JTA transactions, you must bind a JTA DataSource object, also known as an OracleJTADataSource object, to identify each database included in the transaction. See the bindds command of the sess_sh tool in the Oracle9i Java Tools Reference for a description of other DataSource object types.

Single-Phase Commit Scenario

In a single-phase commit scenario, the transaction only includes a single database in the transaction. Since no coordination for updates to multiple databases is needed, you do not need to specify a coordinator. Instead, you simply provide the JNDI bound name and the URL address information for this database within the OracleJTADataSource object. You do not need to provide a database link for a transaction coordinator.

Use the bindds command of the sess_sh tool to bind an DataSource object in the namespace. The full command is detailed in the Oracle9i Java Tools Reference. For example, the following binds an OracleJTADataSource with the name of "/test/empDS" that exists within a single-phase commit transaction with the bindds command:

bindds /test/empDS -url jdbc:oracle:thin:@empHost:5521:ORCL -dstype jta

After binding the DataSource object in the namespace, the server can enlist the database within a global transaction.

Two-Phase Commit Scenario

If multiple databases are to be included in the global transaction, you will need a two-phase commit engine, which is an Oracle9i database that is configured to be the transaction coordinator. Basically, the two-phase commit engine must have database links to each of the databases involved in the transaction. When the transaction ends, the transaction manager notifies the two-phase commit engine to either coordinate the commit of all changes to all involved databases or coordinate a roll back of these same changes.

In order to facilitate this coordination, you must configure the following:

  1. Your system administrator must create fully-qualified public database links from the two-phase commit engine (Oracle9i database) to each database involved in the transaction. These database link names must be included when binding the OracleJTADataSource object.

  2. Bind a JTA DataSource (OracleJTADataSource) object for each database in the transaction. You must include the following in the bindds command:

    1. The JNDI bound name for the object

    2. The URL for creating a connection to the database

    3. The fully-qualified public database link from the two-phase commit engine to this database

The following example binds the empDS JTA DataSource into the namespace with 2pcToEmp as the database link name created on the two-phase commit engine:

% bindds /test/empDS -url jdbc:oracle:thin:@dbsun:5521:ORCL
-dstype jta -dblink 2pcToEmp.oracle.com

Configuring Two-Phase Commit Engine

When multiple databases are included in a global transaction, the changes to these resources must all be committed or rolled back at the same time. That is, when the transaction ends, the transaction manager contacts a coordinator--also known as a two-phase commit engine--to either commit or roll back all changes to all included databases. The two-phase commit engine is an Oracle9i database that is configured with the following:

In order to facilitate this coordination, you must configure the following:

  1. Designate an Oracle9i database as the two-phase commit engine.

  2. Configure fully-qualified public database links (using the CREATE DATABASE LINK command) from the two-phase commit engine to each database that may be involved in the global transaction. This is necessary for the two-phase commit engine to communicate with each database at the end of the transaction. These database link names must be included when binding the JTA DataSource (OracleJTADataSource) object.

  3. Bind a JTA DataSource (OracleJTADataSource) object for each database in the transaction. You must include the following in the bindds command:

    1. The JNDI bound name for the object

    2. The URL for creating a connection to the database

    3. The fully-qualified database link from the two-phase commit engine to this database

      Provide the fully-qualified database link name in the -dblink option of bindds for each individual database when binding that database's DataSource into the namespace.

      bindds /test/empDS -url jdbc:oracle:thin:@empHost:5521:ORCL 
      -dstype jta -dblink 2pcToEmp.oracle.com
      
      


      Note:

      In a two-phase commit scenario, the DataSource object is bound, with respect to the two-phase commit engine. If you change the two-phase commit engine, you must update all database links, and rebind all concerned DataSource and UserTransaction objects. 


  4. Create the user on the two-phase commit engine that facilitates the two-phase commit. This user will open sessions to each resource involved in the transaction and complete the transaction. To do this, the user must be created on each database and granted CONNECT, RESOURCE, and CREATE SESSION privileges. If the user that completes the transaction is different from the user that starts the transaction, you also need to grant the FORCE ANY TRANSACTION privilege. These privileges must be granted on all databases included in the transaction.

    The decision on whether the FORCE ANY TRANSACTION privilege is needed is determined by whether you bound a username and password with the UserTransaction object.

    • If you do not bind a username and password with the UserTransaction, the user that retrieved the UserTransaction will be the same user that is used to perform the commit or rollback for the two-phase commit on all involved databases.

    • If you bind a username and password with the UserTransaction, then this is the username that the two-phase commit will be committed or rolled back with on all involved databases. The transaction will be started by the user that retrieves the UserTransaction object.

    Both types of users must be created, so that it can open a session from the two-phase commit engine to each of the involved databases. Secondly, it must be granted the CONNECT, RESOURCE, CREATE SESSION privileges to be able to connect to each of these databases. For example, if the user that is needed for completing the transaction is SCOTT, you would do the following on the two-phase commit engine and each database involved in the transaction:

    CONNECT SYSTEM/MANAGER;
    CREATE USER SCOTT IDENTIFIED BY SCOTT;
    GRANT CONNECT, RESOURCE, CREATE SESSION TO SCOTT;
    
    
    

    Lastly, if you bound a username and password with the UserTransaction object, it will be using a different username to finalize the transaction than the username used to start the transaction. For this to be allowed, you must grant the FORCE ANY TRANSACTION privileges on each database involved in the transaction in order for two separate users to start and stop the transaction.

    The advantage of binding a username with the UserTransaction is that it is treated as a global user is always committing all transactions started with this UserTransaction object. Thus, if you have more than one JTA transactions, you will only have to create one user and grant privileges to that user on all involved databases.

    For example, if SCOTT is the username bound with the UserTransaction object, you would need to do the following in addition to the previous grant:

    GRANT FORCE ANY TRANSACTION TO SCOTT;
    
    
  5. Bind a UserTransaction into the namespace. You must provide the two-phase commit engine's fully-qualified database address. At this point, you should decide (based on the discussion in step 3) on whether to bind it with a username and password. The following assumes a global username is bound with the UserTransaction.

    bindut /test/myUT -url sess_iiop://dbsun.mycompany.com:2481:ORCL 
    -user SCOTT -password TIGER
  6. Update the Oracle-specific deployment descriptor with the JNDI name for the two-phase commit engine. This name should be included in the <transaction-manager> element. This element only needs to be defined in the bean where the transaction is started. See "Defining Oracle-Specific Elements for Transactions" for more information.

Once configured, the actual code for starting transactions and enlisting databases in the transaction is the same as described in previous sections. The only difference is that you can open connections to more than a single database. Figure 7-5 shows the client invoking EmployeeBean, which opens a connection to its local database and a connection to a remote database.

Figure 7-5 Including Remote Oracle9i Databases in a Global Transaction


Text description of jta_iiop.gif follows
Text description of the illustration jta_iiop.gif

Example 7-11 Client Code

The client initializes its environment, retrieves the EJB reference, and invokes the remote EmployeeBean EJB. The client does not start the transaction. When the client invokes the EmployeeBean, the container starts the transaction.

package client;
import common.*;
import java.util.Hashtable;
import javax.naming.Context;
import javax.naming.InitialContext;
import oracle.aurora.jndi.sess_iiop.ServiceCtx;

public class Client
{
  public static void main (String[] args) throws Exception
  {
    String serviceURL = args [0];
    String jdbcURL = args [1];
    String objectName = args [2];
    String user = args [3];
    String password = args [4];

    // set up the initial context
    Hashtable env = new Hashtable ();
    env.put (Context.URL_PKG_PREFIXES, "oracle.aurora.jndi");
    env.put (Context.SECURITY_PRINCIPAL, user);
    env.put (Context.SECURITY_CREDENTIALS, password);
    env.put (Context.SECURITY_AUTHENTICATION, ServiceCtx.NON_SSL_LOGIN);
    Context ic = new InitialContext (env);    

    // lookup the home and remote interfaces fro the employee
    EmployeeHome home = (EmployeeHome)ic.lookup (serviceURL + objectName);
    EmployeeRemote remote = home.create ();

    // retrieve info abount this employee in this session
    Employee employee = remote.getEmployeeForUpdate ("SCOTT");
    System.out.println ("Beginning salary for " + employee.name + " is " +
			employee.salary);

    // increase salary
    // employee.salary += 0.1 * employee.salary;
    employee.salary += 100;

    // update the infomation in the transaction
    remote.updateEmployee (employee);
  }
}

Example 7-12 EmployeeBean EJB Deployment Descriptor

The EmployeeBean is deployed as a container-managed transaction bean with the RequiresNew attribute. Both of the JDBC resources contained within this transaction are specified in the <resource-ref> elements. The following is the portion of the EJB deployment descriptor that relates to transactions:

<?xml version="1.0"?>
<!DOCTYPE ejb-jar PUBLIC "-//Sun Microsystems Inc.//DTD Enterprise JavaBeans 1.1
//EN" "ejb-jar.dtd">
<ejb-jar>
   <enterprise-beans>
      <session>
         ...
         <transaction-type>Container</transaction-type>
         <resource-ref>
           <res-ref-name>jdbc/EmployeeDS</res-ref-name>
           <res-type>javax.sql.DataSource</res-type>
           <res-auth>Application</res-auth>
         </resource-ref>
         <resource-ref>
           <res-ref-name>jdbc/HRDS</res-ref-name>
           <res-type>javax.sql.DataSource</res-type>
           <res-auth>Application</res-auth>
         </resource-ref>
      </session>
   </enterprise-beans>
   <assembly-descriptor>
      ...
      <container-transaction>
         <method>
            <ejb-name>test/EmployeeBean</ejb-name>
            <method-name>*</method-name>
         </method>
         <trans-attribute>RequiresNew</trans-attribute>
      </container-transaction>
   </assembly-descriptor>
</ejb-jar>

Example 7-13 EmployeeBean Oracle-Specific Deployment Descriptor

  1. The <resource-ref> elements defined in the EJB deployment descriptor are mapped to the JNDI bound names in the Oracle-specific deployment descriptor.

    Both of the databases are bound as JTA DataSource objects in the JNDI namespace, as follows:

    bindds /test/DataSource/empDS -url jdbc:oracle:kprb:@empHost:5521:ORCL 
    -dstype jta -dblink 2pcToEmp.oracle.com bindds /test/DataSource/hrDS -url jdbc:oracle:thin:@HrHost:5521:ORCL
    -dstype jta -dblink 2pcToHR.oracle.com
  2. The UserTransaction that is bound with the two-phase commit engine URL is specified in the <transaction-manager> element.

    The UserTransaction object was previously bound with the bindut command. For example, the following binds "/test/myUT" for this two-phase commit:

    bindut /test/myUT -url sess_iiop://dbsun.mycompany.com:2481:ORCL
    
    
    
  3. The database where the bean is deployed is involved in a two-phase commit scenario. Thus, the <default-enlist> element must be set to FALSE. The only way to enlist a local database in a two-phase commit scenario is to use a JTA DataSource object.

    <?xml version="1.0"?>
    <!DOCTYPE oracle-descriptor PUBLIC "-//Sun Microsystems Inc.//DTD Enterprise Jav
    aBeans 1.1//EN" "oracle-ejb-jar.dtd">
    <oracle-ejb-jar>
     <oracle-descriptor>
      <mappings>
        ...
        <resource-ref-mapping>
          <res-ref-name>jdbc/EmployeeDS</res-ref-name>
          <jndi-name>test/DataSource/empDS</jndi-name>
        </resource-ref-mapping>
        <resource-ref-mapping>
          <res-ref-name>jdbc/HRDS</res-ref-name>
          <jndi-name>test/DataSource/hrDS</jndi-name>
        </resource-ref-mapping>
        <transaction-manager>
          <jndi-name>test/myUT</jndi-name>
          <default-enlist>false</default-enlist>
        </transaction-manager>
      </mappings>
     </oracle-descriptor>
    </oracle-ejb-jar>
    

Example 7-14 EmployeeBean Using Bean-Managed Transactions

The container-managed transactional EmployeeBean retrieves connections to both the local and a remote Oracle9i database within the updateEmployee method.

public class EmployeeBean implements SessionBean
{
  Employee remoteEmployee = null;
  String remoteEmpName = "SMITH";

  ...

  public void updateEmployee (Employee employee)
    throws SQLError, RemoteException
  {
      Context ic = new InitialContext(); // inSession Lookup Context
      // get a connection to the local DB using an environment variable
      //specified in the deployment descriptor
      DataSource localDS = (DataSource)ic.lookup 
	("java:comp/env/jdbc/EmployeeDS");

      // get a connection to the local DB
      Connection localConn =  localDS.getConnection ();

      //retrieve the remote database DataSource HRDS using the environment 
      //variable specified in the deployment descriptor
      DataSource remoteDS = (DataSource)ic.lookup ("java:comp/env/jdbc/HRDS");

      // get a connection to the remote DB passing in the username and 
      // password for this database. (Otherwise, it would have had to be
      // specified in the Context environment.
      Connection remoteConn = remoteDS.getConnection ("scott", "tiger");

      //setup the context for issuing SQLJ against the remote database
      DefaultContext remoteCtx = new DefaultContext (remoteConn);

      //issue SQL DML statements against the local database
      #sql { update emp set ename = :(employee.name), sal = :(employee.salary)
                    where empno = :(employee.number) };

      remoteEmployee.salary += 200;

      //issue SQL DML statements against the remote database
      #sql [remoteCtx] { update emp set ename = :(remoteEmployee.name), 
	sal = :(remoteEmployee.salary) 
	  where empno = :(remoteEmployee.number) };


      //close both database connections
      localConn.close();
      remoteConn.close ();
}

Global Transactions in an Oracle9i Application Server Environment

In a normal global transaction, you open connections to each database that you want included in the database. After the transaction completes, the transaction manager commits all changes to all databases involved in the transaction.

However, with Oracle9i Application Server, the Oracle Database Cache may incorrectly be treated by the transaction manager as one of the databases in the global transaction. A typical application is shown in Figure 7-6. The EJB that is active in the middle-tier retrieves a connection to both the Oracle Database Cache and to the back-end Oracle database. However, the EJB can only update the back-end database. The transaction manager must not treat the database cache as another database involved in the transaction or it will perform a two-phase commit when the transaction ends. The two-phase commit process is expensive and unnecessary. Only the back-end database should be enlisted in the global transaction; thus, prompting the transaction manager to perform a single-phase commit, which is inexpensive.

Figure 7-6 Global Transaction Including Database Cache and Back-End Database


Text description of iasjta.gif follows
Text description of the illustration iasjta.gif

To ensure that the database cache is not treated as an enlisted database in the global transaction, do the following:

  1. Ensure that the <default-enlist> element in the Oracle-specific deployment descriptor is either not set--so that it defaults to FALSE--or is set to FALSE.

  2. Bind the DataSource for the database cache with any non-JTA type. Only a JTA DataSource (OracleJTADataSource) object can be automatically enlisted in a global transaction. The DataSource object bound for the database cache must be bound through the bindds command with any -type other than jta. If bound with bindds -type jta, the database cache will be considered part of the global transaction and the transaction manager will complete the global transaction with a two-phase commit.

Creating DataSource Objects Dynamically

If you want to bind only a single DataSource object in the namespace to be used for multiple database resources, you must do the following:

  1. Bind the DataSource without specifying the URL, host, port, SID, or driver type. Thus, you execute the bindds tool with only the -dstype jta option, as follows:

    sess_sh -service jdbc:oracle:thin:@nsHost:5521:ORCL -user SCOTT -password 
    TIGER
    & bindds /test/empDS -dstype jta
    
    
  2. Retrieve the DataSource in your code. When you perform the lookup, you must cast the returned object to OracleJTADataSource instead of DataSource. The Oracle-specific version of the DataSource class contains methods to set the DataSource properties.

  3. Set the following properties:

    • Set the URL with the OracleJTADataSource.setURL method

    • Fully-qualified database link if using two-phase commit engine with the OracleJTADataSource.setDBLink method

  4. Retrieve the connection through the OracleJTADataSource.getConnection method as indicated in the other examples.

Example 7-15 Retrieving Generic DataSource

The following example retrieves a generically bound DataSource from the namespace using in-session lookup and initializes all relevant fields.

//retrieve an in-session generic DataSource object
OracleJTADataSource ds =
(OracleJTADataSource)ic.lookup ("java:comp/env/test/empDS"); //set all relevant properties for my database //URL is for a local database so use the KPRB URL ds.setURL ("jdbc:oracle:kprb:"); //Used in two-phase commit, so provide the fully qualified database link that //was created from the two-phase commit engine to this database ds.setDBLink("localDB.oracle.com"); //Finally, retrieve a connection to the local database using the DataSource Connection conn = ds.getConnection ();

Setting the Transaction Timeout

A resource transaction automatically has an idle timeout of 60 seconds. If the database session attached to the transaction is idle for over the timeout limit, the transaction is rolled back. That is, if you close the connection to one of the databases in the transaction, the timeout starts at that point. If you have not completed the transaction by the time the timer is up, the transaction is rolled back. However, if you reopen another connection to the same database, then the timer stops. It will restart at zero when the reopened connection is closed.

To initialize a different timeout, set the timeout value--in seconds--through the setTransactionTimeout method before the transaction is begun. If you change the timeout value after the transaction begins, it will not affect the current transaction. The following example sets the timeout to 2 minutes (120 seconds) before the transaction begins.

//create the initial context
InitialContext ic = new InitialContext ( );

//retrieve the UserTransaction object
ut = (UserTransaction)ic.lookup ("/test/myUT");

//set the timeout value to 2 minutes
ut.setTransactionTimeout (120);

//begin the transaction
ut.begin

//Update employee table with new employees
updateEmployees(emp, newEmp);

//end the transaction.
ut.commit ();

Using the Session Synchronization Interface

An EJB that is a session bean can optionally implement the session synchronization interface, to be notified by the container of the transactional state of the bean. The following methods are specified in the javax.ejb.SessionSynchronization interface:

afterBegin

public abstract void afterBegin() throws RemoteException

The afterBegin() method notifies a session Bean instance that a new transaction has started, and that the subsequent methods on the instance are invoked in the context of the transaction.

A bean can use this method to read data from a database and cache the data in the bean's fields.

This method executes in the proper transaction context.

beforeCompletion

public abstract void beforeCompletion() throws RemoteException

The container calls the beforeCompletion() method to notify a session bean that a transaction is about to be committed. You can implement this method to, for example, write any cached data to the database.

afterCompletion

public abstract void afterCompletion(boolean committed) throws RemoteException

The container calls afterCompletion() to notify a session bean that a transaction commit protocol has completed. The parameter tells the bean whether the transaction has been committed or rolled back.

This method executes with no transaction context.

Example 7-16 SessionSynchronization Example

In order for the container to invoke your bean implementation before and after every transaction, your bean must implement the SessionSynchronization interface.

package employeeServer;

import employee.*;

import javax.ejb.SessionBean;
import javax.ejb.CreateException;
import javax.ejb.SessionContext;
import java.rmi.RemoteException;

import java.sql.SQLException;

public class EmployeeBean implements SessionBean
implements SessionSynchronization { // Methods of the Employee interface public EmployeeInfo getEmployee (String name) throws RemoteException, SQLException { int empno = 0; double salary = 0.0; #sql { select empno, sal into :empno, :salary from emp where ename = :name }; return new EmployeeInfo (name, empno, salary); } public void updateEmployee (EmployeeInfo employee) throws RemoteException, SQLException { #sql { update emp set ename = :(employee.name), sal = :(employee.salary) where empno = :(employee.number) }; return; } // Methods of the SessionBean public void ejbCreate () throws RemoteException, CreateException {} public void ejbRemove () {} public void setSessionContext (SessionContext ctx) {} public void ejbActivate () {} public void ejbPassivate () {} public void beforeBegin() { ... perform work ... } public void afterCompletion() { ... perform work ...
} }

JTA Limitations

The following are the portions of the JTA specification that Oracle9i does not support.

Nested Transactions

Nested transactions are not supported in this release. If you attempt to begin a new transaction before committing or rolling back any existing transaction, the transaction service throws a NotSupportedException exception.

Interoperability

The transaction services supplied with this release do not interoperate with other JTA implementations.

Timeouts

The global transaction timeout does not work. In addition, the UserTransaction idle timeout (setTransactionTimeout method) starts only when a database connection is closed and idle. Oracle recommends that you do not use timeouts.

JDBC Restrictions

If you are using JDBC calls in your bean to update a database, and you have an active transaction context, you should not also use JDBC to perform transaction services, by calling methods on the JDBC connection. Do not code JDBC transaction management methods. For example:

Connection conn = ...
...
conn.commit();  // DO NOT DO THIS!!

Doing so will cause a SQLException to be thrown. Instead, you must commit using the UserTransaction object retrieved to handle the global transaction. When you commit using the JDBC connection, you are instructing a local transaction to commit, not the global transaction. When the connection is involved in a global transaction, trying to commit a local transaction within the global transaction causes an error to occur.

In the same manner, you must also avoid doing direct SQL commits or rollbacks through JDBC. Code the bean to either handle transactions directly using the UserTransaction interface or let the bean container manage the bean transactions.

Within a global transaction, you cannot execute a local transaction. If you try, the following error will be thrown:

ORA-2089 "COMMIT is not allowed in a subordinate session."

Some SQL commands implicitly execute a local transaction. All SQL DDL statements, such as "CREATE TABLE", implicitly starts and commits a local transaction under the covers. If you are involved in a global transaction that has enlisted the database that the DDL statement is executing against, the global transaction will fail.


Go to previous page Go to next page
Oracle
Copyright © 1996-2001, Oracle Corporation.

All Rights Reserved.
Go To Documentation Library
Home
Go To Product List
Book List
Go To Table Of Contents
Contents
Go To Index
Index

Master Index

Feedback