Skip Headers
Oracle® TopLink Developer's Guide
10g Release 3 (10.1.3.1.0)

Part Number B28218-01
Go to Documentation Home
Home
Go to Book List
Book List
Go to Table of Contents
Contents
Go to Index
Index
Go to Feedback page
Contact Us

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

97 Understanding TopLink Transactions

This chapter explains how transactions are implemented in TopLink, including the following:

Unit of Work Architecture

A database transaction is a set of operations (create, read, update, or delete) that either succeed or fail as a single operation. The database discards, or rolls back, unsuccessful transactions, leaving the database in its original state. Transactions may be internal (that is, provided by TopLink) or external (that is, provided by a source external to the application, such as an application server).

In TopLink, transactions are contained in the unit of work object. You acquire a unit of work from a session (see "Acquiring a Unit of Work") and using its API, you can control transactions directly or through a Java 2 Enterprise Edition (J2EE) application server transaction controller such as the Java Transaction API (JTA).

Transactions execute in their own context, or logical space, isolated from other transactions and database operations.

The transaction context is demarcated; that is, it has a defined structure that includes:

The degree to which concurrent (parallel) transactions on the same data are allowed to interact is determined by the level of transaction isolation configured. ANSI/SQL defines four levels of database transaction isolation as shown in Table 97-1. Each offers a trade-off between performance and resistance from the following unwanted actions:

Table 97-1 Transaction Isolation Levels

Transaction Isolation Level Dirty Read Nonrepeatable Read Phantom Read

Read Uncommitted

Yes

Yes

Yes

Read Committed

No

Yes

Yes

Repeatable Read

No

No

Yes

Serializable

No

No

No


As a transaction is committed, the database maintains a log of all changes to the data. If all operations in the transaction succeed, the database allows the changes; if any part of the transaction fails, the database uses the log to roll back the changes.

Like any transaction, a unit of work transaction provides the following:

Unit of Work Transaction Context

Unit of work operations occur within a unit of work context, in which writes are isolated from the database until commit time. The unit of work executes changes on copies, or clones, of objects in its own internal cache, and if successful, applies changes to objects in the database and the session cache.

Unit of Work Transaction Demarcation

In a standalone TopLink application, your application demarcates transactions using the unit of work.

If your application includes a J2EE container that provides container-managed transactions, your application server demarcates transactions using its own transaction service. You configure TopLink to integrate with the container's transaction service by specifying a TopLink external transaction controller.

A TopLink external transaction controller class integrates the unit of work with an application server's transaction service. Using an external transaction controller, your application can participate in transactions that span multiple data sources and that are managed by the application server. The external transaction controller coordinates messages and callbacks between the application server's transaction service and the unit of work.

When you configure your application to use an external transaction controller (see "Configuring the Server Platform"), the unit of work executes as part of an external transaction. The unit of work still manages its own internal operations, but it waits for the external transaction to tell it to write changes back to the database and to the session cache.

Note that because the transaction happens outside of the unit of work context and is controlled by the application server's transaction service, errors can be more difficult to diagnose and fix because exceptions may occur outside of your application code, for example, during application server initiated call-backs.

You can integrate the unit of work with the following:

JTA Controlled Transactions

The Java Transaction API (JTA) is the application programming interface you use to interact with a transaction manger.

Using JTA, your application can participate in a distributed transaction. A transaction manager that implements JTA provides transaction management and connection pooling and enables your application to interact with multiple data sources transparently by using JTA.

For more information, see "Integrating the Unit of Work With an External Transaction Service".

OTS Controlled Transactions

The CORBA Object Transaction Service (OTS) specification is part of the Common Object Request Brokers Architecture (CORBA) Object Services model and is the standard for Object Request Broker (ORB) implementations. Some servers implement the Java APIs for the OTS rather than for JTA (see "JTA Controlled Transactions").

Use TopLink OTS support with the unit of work to directly access the Java OTS interfaces of servers that do not support JTA.

To integrate your application with an OTS transaction service, you must configure your application to use a custom server platform (see "Configuring the Server Platform").

For more information, see "Integrating the Unit of Work With an External Transaction Service".

CMP Controlled Transactions

Entity beans that use container-managed persistence may participate in transactions that are either client demarcated or container demarcated.

A client demarcated transaction occurs when a client of an entity bean directly sets up transaction boundaries using the javax.transaction.UserTransaction interface.

A container demarcated transaction occurs when the container automatically wraps an invocation on an EJB in a transaction based upon the transaction attributes supplied in the EJB deployment descriptor.

In transactions involving EJB, TopLink waits until the transaction begins its two-phase commit process before updating the database. This allows for the following:

  • SQL optimizations that ensure only changed data is written to the data source

  • Proper ordering of updates to allow for database constraints

For more information, see "Integrating the Unit of Work With CMP".

Unit of Work Transaction Isolation

The unit of work does not directly participate in database transaction isolation. Because the unit of work may execute queries outside the database transaction (and, by interacting with the cache, outside the database itself), the database does not have control over this data and its visibility.

However, by default, TopLink provides a degree of transaction isolation regardless of what database transaction isolation has been configured on the underlying database.

Each unit of work instance operates on its own copy (clone) of registered objects (see "Clones and the Unit of Work"). In this case, because the unit of work provides an API that allows querying to be done on object changes within a unit of work (see "Using Conforming Queries and Descriptors"), the unit of work provides read committed operations.

Optimistic locking, optimistic read locking, or pessimistic locking can be used to further manage concurrency (see "Locking and the Unit of Work").

Changes are committed to the database only when the unit of work commit method is called (either directly or by way of an external transaction controller).

For detailed information on configuring and using TopLink to achieve a particular level of transaction isolation and transaction isolation level limitations, see "Database Transaction Isolation Levels".

Unit of Work Concepts

This section introduces transaction concepts unique to TopLink, including the following:

Unit of Work Benefits

The TopLink unit of work simplifies transactions and improves transactional performance. It is the preferred method of writing to a database in TopLink because it performs the following:

  • Sends a minimal amount of SQL to the database during the commit by updating only the exact changes down to the field level

  • Reduces database traffic by isolating transaction operations in their own memory space

  • Optimizes cache coordination, in applications that use multiple caches, by passing change sets (rather than objects) between caches

  • Isolates object modifications in their own transaction space to allow parallel transactions on the same objects

  • Ensures referential integrity and minimizes deadlocks by automatically maintaining SQL ordering

  • Orders database insert, update, and delete operations to maintain referential integrity for mapped objects

  • Resolves bidirectional references automatically

  • Frees the application from tracking or recording its changes

  • Simplifies persistence with persistence by reachability (see "Associating a New Source to an Existing Target Object")

Unit of Work Life Cycle

TopLink uses the unit of work as follows:

  1. The client application acquires a unit of work from a session object.

  2. The client application queries TopLink to obtain a cache object it wants to modify, and then registers the cache object with the unit of work.

  3. The unit of work registers the object according to the object's change policy. For more information about how change policy affects registration, see "Unit of Work and Change Policy".

    By default, as each object is registered, the unit of work accesses the object from the session cache or database and creates a backup clone and working clone (see "Clones and the Unit of Work"). The unit of work returns the working clone to the client application.

  4. The client application modifies the working object returned by the unit of work.

  5. The client application (or external transaction controller) commits the transaction.

  6. The unit of work calculates the change set for each registered object according to the object's change policy. For more information about how change policy affects change set calculation, see "Unit of Work and Change Policy".

    By default, at commit time, the unit of work compares the working clones to the backup clones and calculates the change set (that is, determines the minimum changes required). The comparison is done with a backup clone so that concurrent changes to the same objects will not result in incorrect changes being identified. The unit of work then attempts to commit any new or changed objects to the database.

    If the commit transaction succeeds, the unit of work merges changes into the shared session cache. Otherwise, no changes are made to the objects in the shared cache. For more details, see "Commit and Rollback Transactions".

    If there are no changes, the unit of work does not start a new transaction.

Figure 97-1 The Life Cycle of a Unit of Work

Description of Figure 97-1 follows
Description of "Figure 97-1 The Life Cycle of a Unit of Work"

Example 97-1 shows the default life cycle in code.

Example 97-1 Unit of Work Life Cycle

// The application reads a set of objects from the database 
Vector employees = session.readAllObjects(Employee.class);

// The application specifies an employee to edit
. . .
Employee employee = (Employee) employees.elementAt(index);

try {
    // Acquire a unit of work from the session
    UnitOfWork uow = session.acquireUnitOfWork();
    // Register the object that is to be changed. Unit of work returns a clone
    // of the object and makes a backup copy of the original employee
    Employee employeeClone = (Employee)uow.registerObject(employee);
    // Make changes to the employee clone by adding a new phoneNumber. 
    // If a new object is referred to by a clone, it does not have to be
    // registered. Unit of work determines it is a new object at commit time
    PhoneNumber newPhoneNumber = new PhoneNumber("cell","212","765-9002");
    employeeClone.addPhoneNumber(newPhoneNumber);
    // Commit the transaction: unit of work compares the employeeClone with
    // the backup copy of the employee, begins a transaction, and updates the
    // database with the changes. If successful, the transaction is committed
    // and the changes in employeeClone are merged into employee. If there is an
    // error updating the database, the transaction is rolled back and the
    // changes are not merged into the original employee object
    uow.commit();
} catch (DatabaseException ex) {
    // If the commit fails, the database is not changed. The unit of work should
    // be thrown away and application-specific action taken
}
// After the commit, the unit of work is no longer valid. Do not use further

Unit of Work and Change Policy

The unit of work tracks changes for a registered object based on the change policy you configure for the object's descriptor. If there are no changes, the unit of work will not start a new transaction.

Table 97-2 lists the change policies that TopLink provides.

Table 97-2 TopLink Change Policies

Change Policy Applicable to...

Deferred Change Detection Policy


Wide range of object change characteristics.

The default change policy.

Object-Level Change Tracking Policy


Objects with few attributes or with many attributes and many changed attributes.

Attribute Change Tracking Policy


Objects with many attributes and few changed attributes.

The most efficient change policy.

The default change policy for EJB 3.0 persistent or 2.n CMP on OC4J.


For more information, see "Configuring Change Policy".

Deferred Change Detection Policy

The DeferredChangeDetectionPolicy is the change policy that all persistent objects use by default.

This option provides good unit of work commit performance for a wide range of object change characteristics.

When you register in a unit of work an object whose descriptor is configured with a DeferredChangeDetectionPolicy (see "Configuring Deferred Change Detection Policy"), a backup clone is made of the object (see "Clones and the Unit of Work") and at commit time, the unit of work computes changes by making an attribute-by-attribute comparison between the backup clone and the original object.

This change policy is applicable to all mapping types.

Object-Level Change Tracking Policy

The ObjectChangeTrackingPolicy optimizes the unit of work commit transaction by including objects in the change set calculation only if at least one attribute has changed.

This option provides improved unit of work commit performance for objects with few attributes, or with many attributes and many changed attributes.

When you register in a unit of work an object whose descriptor is configured with ObjectChangeTracking change policy, a backup clone is made of the object and at commit time, the unit of work computes changes by comparing the backup to the current object if and only if at least one attribute is changed (if the object's hasChanges method returns true). If a registered object has no changes, the unit of work does not compare it to the backup clone.

This change policy is applicable to a subset of mapping types (see "Change Policy Mapping Support").

TopLink provides different levels of support for this change policy depending on the EBJ version and application server you are using:

EJB CMP

For CMP applications deployed to an application server for which TopLink provides CMP integration (see "Application Server Support"), when you configure a descriptor for an entity bean with container-managed persistence with an ObjectChangeTrackingPolicy, TopLink code generates a concrete subclass to implement the TopLink ChangeTracker interface at deploy time (see "Configuring Object Change Tracking Policy").


Note:

The preceding paragraph is also true for applications that use EJB 3.0 persistence.

Attribute Change Tracking Policy

The AttributeChangeTrackingPolicy optimizes the unit of work commit transaction by tracking all object changes at the attribute level. This eliminates two unit of work operations: backup clone creation and change detection through comparison.

This option provides improved unit of work commit performance for objects with many attributes, and few changed attributes. Generally, this is the most efficient change policy.

This change policy is applicable to a subset of mapping types (see "Change Policy Mapping Support").


Note:

You cannot use the AttributeChangeTrackingPolicy if you are using any instance of FieldsLockingPolicy (see "Optimistic Field Locking Policies").

TopLink provides different levels of support for this change policy depending on the EBJ version and application server you are using:

Plain Java Objects or Other Application Servers

For plain Java objects or application servers other than OC4J, to use the AttributeChangeTrackingPolicy with a class, you must configure the class's descriptor with an AttributeChangeTrackingPolicy and you must implement the ChangeTracker interface in that class (see "Configuring Attribute Change Tracking Policy").

EJB CMP on OC4J

When using CMP on OC4J, if you want to benefit from this performance enhancement, configure your descriptors with the default DeferredChangeDetectionPolicy and allow TopLink to automatically apply an AttributeChangeTrackingPolicy. If you configure your project's descriptors with any other change policy, TopLink will honor that configuration and not apply an AttributeChangeTrackingPolicy.

When you deploy a TopLink-enabled CMP application to OC4J, for each mapped class configured with the default DeferredChangeDetectionPolicy, TopLink uses code generation to automatically override this configuration with an AttributeChangeTrackingPolicy and to make the class implement the required interfaces.


Note:

The preceding information is applicable to applications built using EJB 3.0.

However, when you deploy a TopLink-enabled EJB 3.0 persistent application to OC4J, for each mapped class configured with the default DeferredChangeDetectionPolicy, TopLink uses bytecode weaving to automatically override this configuration with an AttributeChangeTrackingPolicy and to make the class implement the required interfaces.


Change Policy Mapping Support

TopLink supports alternative change tracking policies (policies other than DeferredChangeDetectionPolicy) for attributes that use any of the following mapping types:

TopLink uses the DeferredChangeDetectionPolicy (see "Deferred Change Detection Policy") for attributes that use any other type of mapping.

Clones and the Unit of Work

When using the DefrerredChangeDetectionPolicy or the ObjectLevelChangeTrackingPolicy (see "Deferred Change Detection Policy"), the unit of work maintains two copies of the original objects registered with it:

  • Working clones

  • Backup clones

After you change the working clones and the transaction is committed, the unit of work compares the working clones to the backup clones, and writes any changes to the database. The unit of work uses clones to allow parallel units of work (see "Nested and Parallel Units of Work") to exist, a requirement in multiuser three-tier applications.

The TopLink cloning process is efficient in that it clones only the mapped attributes of registered objects, and stops at indirection objects unless you trigger the indirection. For more information, see "Configuring Indirection".

You can customize the cloning process using the descriptor's copy policy. For more information, see "Configuring Copy Policy".

Never use a clone after committing the unit of work that the clone is from (even if the transaction fails and rolls back). A clone is a working copy used during a transaction and as soon as the transaction is committed (successful or not), the clone must not be used. Accessing an uninstantiated clone value holder after a unit of work commit transaction will raise an exception. The only time you can use a clone after a successful commit transaction is when you use the advanced API described in "Resuming a Unit of Work After Commit".

Nested and Parallel Units of Work

You can use TopLink to create the following:

For additional information and examples on using nested and parallel units of work, see "Using a Nested or Parallel Unit of Work".

Nested Unit of Work

You can nest a unit of work (the child) within another unit of work (the parent). A nested unit of work does not commit changes to the database. Instead, it passes its changes to the parent unit of work, and the parent attempts to commit the changes at commit time. Nesting units of work lets you break a large transaction into smaller isolated transactions, and ensures that:

  • Changes from each nested unit of work commit or fail as a group.

  • Failure of a nested unit of work does not affect the commit or rollback transaction of other operations in the parent unit of work.

  • Changes are presented to the database as a single transaction.

Parallel Unit of Work

You can modify the same objects in multiple unit of work instances in parallel because the unit of work manipulates copies of objects. TopLink resolves any concurrency issues when the Units of Work commits the changes.

Commit and Rollback Transactions

When a unit of work transaction is committed, it either succeeds, or fails and rolls back. A commit transaction can be initiated by your application or by a J2EE container.

Commit Transactions

At commit time, the unit of work compares the working clones and backup clones to calculate the change set (that is, to determine the minimum changes required). Changes include updates to or deletion of existing objects, and the creation of new objects. The unit of work then begins a database transaction, and attempts to write the changes to the database. If all changes commit successfully on the database, the unit of work merges the changed objects into the session cache. If any one of the changes fail on the database, the unit of work rolls back any changes on the database, and does not merge changes into the session cache.

The unit of work calculates commit order using foreign key information from one-to-one and one-to-many mappings. If you encounter constraint problems during a commit transaction, verify your mapping definitions. The order in which you register objects with the registerObject method does not affect the commit order.

Commit and JTA

When your application uses JTA, the unit of work commit transaction acts differently than in a non-JTA application. In most cases, the unit of work attaches itself to an external transaction. If no transaction exists, the unit of work creates a transaction. This distinction affects commit activity as follows:

  • If the unit of work attaches to an existing transaction, the unit of work ignores the commit call. The transaction commits the unit of work when the entire external transaction is complete.

  • If the unit of work starts the external transaction, the transaction treats the unit of work commit call as a request to commit the external transaction. The external transaction then calls its own commit code on the database.

In either case, only the external transaction can call commit on the database because it owns the database connection.

For more information, see "Integrating the Unit of Work With an External Transaction Service".

Rollback Transactions

A unit of work commit transaction must succeed or fail as a unit. Failure in writing changes to the database causes the unit of work to roll back the database to its previous state. Nothing changes in the database, and the unit of work does not merge changes into the session cache.

Rollback and JTA

In a JTA environment, the unit of work does not own the database connection. In this case, the unit of work sends the rollback call to the external transaction rather than the database, and the external transaction treats the rollback call as a request to roll the transaction back.

For more information, see "Integrating the Unit of Work With an External Transaction Service".

Primary Keys

You cannot modify the primary key attribute of an object in a unit of work. This is an unsupported operation and doing so will result in unexpected behaviour (exceptions or database corruption).

To replace one instance of an object with unique constraints with another, see "Using the Unit of Work setShouldPerformDeletesFirst Method".

Unit of Work Optimization

By default, the unit of work performs change set calculation efficiently for a wide range of object change characteristics. However, there are various ways you can use the unit of work to enhance application performance.

One way to improve performance is to consider using an alternative change policy (see "Unit of Work and Change Policy").

For more performance options, see "Unit of Work Optimization".

Understanding the Unit of Work API

You do not instantiate an instance of oracle.toplink.sessions.UnitOfWork. Rather, you acquire a unit of work from an instance of oracle.toplink.sessions.Session or from another unit of work.

For more information on creating sessions, see "Creating Sessions".

For more information on acquiring a unit of work, see "Acquiring a Unit of Work".

For more information on using the basic API of the unit of work, see "Using Basic Unit of Work API".

For more information on using the advanced API of the unit of work, see "Using Advanced Unit of Work API".

Unit of Work as Session

The unit of work extends the interface oracle.toplink.sessions.Session, and implements all the usual session API. When using session API from a unit of work, you should consider the following:

Reading and Querying Objects with the Unit of Work

A unit of work offers the same set of database access methods as a regular session.

When called from a unit of work, these methods access the objects in the unit of work, register the selected objects automatically, and return clones.

Although this makes it unnecessary for you to call the registerObject and registerAllObjects methods, be aware of the restrictions on registering objects described in "Creating an Object" and "Associating a New Source to an Existing Target Object".

Reading Objects with the Unit of Work

As with regular sessions, you use the readObject and readAllObjects methods to read objects from the database.

Querying Objects with the Unit of Work

You can execute queries in a unit of work with the executeQuery method.


Note:

Because a unit of work manages changes to existing objects and the creation of new objects, modifying queries such as InsertObjectQuery or UpdateObjectQuery are not necessary and therefore are not supported by the unit of work.

Locking and the Unit of Work

For information on locking API generic to all sessions, see:

For information on locking API specific to a unit of work, see "Using Optimistic Read Locking With forceUpdateToVersionField".

Example Model Object and Schema

Throughout the chapters in this part, the following object model and schema are used in the examples provided. The example object model appears in Figure 97-2 and the example entity-relationship (data model) diagram appears in Figure 97-3.

Figure 97-2 Example Object Model

Description of Figure 97-2 follows
Description of "Figure 97-2 Example Object Model"

Figure 97-3 Example Data Model

Description of Figure 97-3 follows
Description of "Figure 97-3 Example Data Model"