Oracle® Application Development Framework Developer's Guide
10g Release 3 (10.1.3) B25386-01 |
|
![]() Previous |
![]() Next |
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.
In TopLink, transactions are contained in the unit of work object. You acquire a unit of work from a session 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).
The unit of work isolates changes in a transaction from other threads until it successfully commits the changes to the database. Unlike other transaction mechanisms, the unit of work automatically manages changes to the objects in the transaction, the order of the changes, and changes that might invalidate other TopLink caches. The unit of work manages these issues by calculating a minimal change set, ordering the database calls to comply with referential integrity rules and deadlock avoidance, and merging changed objects into the shared cache. In a clustered environment, the unit of work also synchronizes changes with the other servers in the coordinated cache.
Example 3-7 shows how to acquire a unit of work from a client session object.
Example 3-7 Acquiring a Unit of Work
public UnitOfWork acquireUnitOfWork() { Server server = getServer(); if (server.hasExternalTransactionController()) { return server.getActiveUnitOfWork(); server.acquireUnitOfWork(); }
When you create new objects in the unit of work, use the registerObject
method to ensure that the unit of work writes the objects to the database at commit time.
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.
Example 3-8 and Example 3-9 show how to create and persist a simple object (without relationships) using the clone returned by the unit of work registerObject
method.
Example 3-8 Creating an Object: Preferred Method
UnitOfWork uow = session.acquireUnitOfWork(); Pet pet = new Pet(); Pet petClone = (Pet)uow.registerObject(pet); petClone.setId(100); petClone.setName("Fluffy"); petClone.setType("Cat"); uow.commit();
Example 3-9 shows a common alternative.
Example 3-9 Creating an Object: Alternative Method
UnitOfWork uow = session.acquireUnitOfWork(); Pet pet = new Pet(); pet.setId(100); pet.setName("Fluffy"); pet.setType("Cat"); uow.registerObject(pet); uow.commit();
Both approaches produce the following SQL:
INSERT INTO PET (ID, NAME, TYPE, PET_OWN_ID) VALUES (100, 'Fluffy', 'Cat', NULL)
Example 3-8 is preferred: it gets you into the pattern of working with clones and provides the most flexibility for future code changes. Working with combinations of new objects and clones can lead to confusion and unwanted results.
TopLink uses the unit of work as follows:
The client application acquires a unit of work from a session object.
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.
The unit of work registers the object according to the object's 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. The unit of work returns the working clone to the client application.
The client application modifies the working object returned by the unit of work.
The client application (or external transaction controller) commits the transaction.
The unit of work calculates the change set for each registered object according to the object's 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. If there are no changes, the unit of work does not start a new transaction.
Figure 3-14 The Life Cycle of a Unit of Work
Example 3-10 shows the default life cycle in code.
Example 3-10 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
In Example 3-11, a Pet
is read prior to a unit of work: the variable pet
is the cache copy clone for that Pet
. Inside the unit of work, register the cache copy to get a working copy clone. We then modify the working copy clone and commit the unit of work.
Example 3-11 Modifying an Object
// Read in any pet
Pet pet = (Pet)session.readObject(Pet.class);
UnitOfWork uow = session.acquireUnitOfWork();
Pet petClone = (Pet) uow.registerObject(pet);
petClone.setName("Furry");
uow.commit();
In Example 3-12, we take advantage of the fact that you can query through a unit of work and get back clones, saving the registration step. However, the drawback is that we do not have a handle to the cache copy clone.
If we wanted to do something with the updated Pet
after the commit transaction, we would have to query the session to get it (remember that after a unit of work is committed, its clones are invalid and must not be used).
Example 3-12 Modifying an Object: Skipping the Registration Step
UnitOfWork uow = session.acquireUnitOfWork(); Pet petClone = (Pet) uow.readObject(Pet.class); petClone.setName("Furry"); uow.commit();
Both approaches produce the following SQL:
UPDATE PET SET NAME = 'Furry' WHERE (ID = 100)
Take care when querying through a unit of work. All objects read in the query are registered in the unit of work and therefore will be checked for changes at commit time. Rather than do a ReadAllQuery
through a unit of work, it is better for performance to design your application to do the ReadAllQuery
through a session, and then register in a unit of work only the objects that need to be changed.
To delete objects in a unit of work, use the deleteObject
or deleteAllObjects
method. When you delete an object that is not already registered in the unit of work, the unit of work registers the object automatically.
When you delete an object, TopLink deletes the object's privately owned child parts, because those parts cannot exist without the owning (parent) object. At commit time, the unit of work generates SQL to delete the objects, taking database constraints into account.
If there are cases where you have objects that will not be garbage collected through privately owned relationships (especially root objects in your object model), then you can explicitly tell TopLink to delete the row representing the object using the deleteObject
API, as shown in Example 3-13.
The TopLink unit of work is a powerful transaction model. In addition to the items listed in this section, you should review the "Understanding TopLink Transactions" chapter in the Oracle TopLink Developer's Guide.
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 3-1 lists the change policies that TopLink provides.
Table 3-1 TopLink Change Policies
Change Policy | Applicable to... |
---|---|
|
Wide range of object change characteristics. The default change policy. |
|
Objects with few attributes or with many attributes and many changed attributes. |
|
Objects with many attributes and few changed attributes. The most efficient change policy. The default change policy for EJB 3.0 or 2.x CMP on OC4J. |
You can use TopLink to create the following:
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.