Skip Headers
Oracle® Application Server TopLink Application Developer's Guide
10g Release 2 (10.1.2)
Part No. B15901-01
  Go To Documentation Library
Home
Go To Product List
Solution Area
Go To Table Of Contents
Contents
Go To Index
Index

Previous
Previous
Next
Next
 

Locking Policy

A locking policy is an important component of any multiuser OracleAS TopLink application. When users share objects in an application, a locking policy ensures that two or more users do not attempt to modify the same object or its underlying data simultaneously.

OracleAS TopLink works with relational databases to provide support for several types of locking policy, including:

Using Optimistic Locking

Optimistic locking, also known as write locking, allows unlimited read access to a given object, but allows a client to modify the object only if the object has not changed since the client last read it.

Optimistic locking checks a version of an object at transaction commit time against the version read during the transaction. This check ensures that no other client modified the data after it was read by the current transaction. If this check detects stale data, the check raises an OptimisticLockException, and the commit fails.


Note:

Using optimistic locking by itself does not protect against having different copies of the same object existing in multiple nodes. For more information, see ÒOptimistic LockingÓ in the Oracle Application Server TopLink Mapping Workbench User's Guide.

Set optimistic locking on the descriptor using one of two locking policies:

  • Version locking policies enforce optimistic locking using a version field (or write lock field). OracleAS TopLink updates this field each time it modifies a record. Add a version field to the table for this purpose.

  • Field locking policies enforce optimistic locking by preventing other processes from writing to the field until the current transaction commits. Field locking does not require additional fields in the table, but you must commit changes to the database using a Unit of Work to implement this type of policy.

For more information about locking policies, see "Two Different Locking Policies".

Advantages and Disadvantages of Optimistic Locking

Here are the advantages of optimistic locking:

  • It prevents users and applications from editing stale data.

  • It notifies users of any locking violation immediately, when updating the object.

  • It does not require you to lock up the database resource.

  • It prevents database deadlocks.

However, optimistic locking cannot prevent applications from selecting and attempting to modify the same data. When two different processes modify data, the first one to commit the changes succeeds; the other process fails and receives an OptimisticLockException.

Advanced Optimistic Locking Policies

All OracleAS TopLink optimistic locking policies implement the OptimisticLockingPolicy interface. This interface includes several methods that you can implement to customize the optimistic locking policy.

For more information about these methods, see the Oracle Application Server TopLink API Reference.

Optimistic Read Locking

Optimistic read lock is an advanced type of optimistic lock that enables you to force lock checking on objects that are not modified by the current transaction. Optimistic read lock also offers the option to increment the unchanged object version or leave the version unchanged.

For example, consider a transaction that updates a mortgage rate by multiplying the central bank prime rate by 1.25. The transaction executes an optimistic read lock on the central prime rate at commit time to ensure that the prime rate has not changed since the transaction began. Note that in this example, the transaction does not increment the version of the unchanged object (the central prime rate).

Example 5-17 Optimistic Read Lock with No Version Increment

try {
    UnitOfWork uow = session.acquireUnitOfWork();
    MortgageRate cloneMortgageRate = (MortgageRate)
      uow.registerObject(mortgageRate);
    CentralPrimeRate cloneCentralPrimeRate = (CentralPrimeRate)
      uow.registerObject(CentralPrimeRate);
    /* Change the Mortgage Rate */
    cloneMortgageRate.setRate(cloneCentralPrimeRate.getRate() * 1.25);
    /* Optimistic read lock check on Central prime rate with no version update*/
    uow.forceUpdateToVersionField(cloneCentralPrimeRate, false);
    uow.commit();
} catch(OptimisticLockException exception) {
    /* Refresh the out-of-date object */
    session.refreshObject(exception.getObject());
    /* Retry… */
}

Consider another example, in which an invoice thread calculates an invoice for a customer. If another thread (the service thread) adds a service to the same customer or modifies the current service, it must inform the invoice thread, which adds the changes to the invoice. This feature is available for objects that implement a version of field locking policy or timestamp locking policy. When you update an object that implements a version locking policy, the version value is incremented or set to the current timestamp.

For more information about field locking policies, see "Field Locking Policies".

Example 5-18 Optimistic Read Lock with Version Increment

/* The following code represents the service thread. Notice that the thread forces a version update. */
try {
    UnitOfWork uow = session.acquireUnitOfWork();
    Customer cloneCustomer = (Customer uow.registerObject(customer);
    Service cloneService = (Service uow.registerObject(service);
    /* Add a service to customer */
    cloneService.setCustomer(cloneCustomer);
    cloneCustomer.getServices().add(cloneService);
    /* Modify the customer version to inform other application that 
      the customer has changed */
    uow.forceUpdateToVersionField(cloneCustomer, true);
    uow.commit();
}
catch (OptimisticLockException exception) {
    /* Refresh out-of-date object */
    session.refreshObject(exception.getObject());
    /* Retry… */
}

/* The following code represents the invoice thread, and calculates a bill for the customer. Notice that it does not force an update to the version */

try {
    UnitOfWork uow = session.acquireUnitOfWork();
    Customer cloneCustomer = (Customer) uow.registerObject(customer);
    Invoice cloneInvoice = (Invoice) uow.registerObject(new Invoice());
    cloneInvoice.setCustomer(cloneCustomer);
    /* Calculate services' charge */
    int total = 0;
    for(Enumeration enum = cloneCustomer.getServices().elements();     enum.hasMoreElements();) {
    total += ((Service) enum.nextElement()).getCost();
    }
    cloneInvoice.setTotal(total);
    /* Force optimistic lock checking on the customer to guarantee a valid 
      calculation */
    uow.forceUpdateToVersionField(cloneCustomer, false);
    uow.commit();
}
catch(OptimisticLockException exception) {
    /* Refresh the customer and its privately owned parts */
    session.refreshObject(cloneCustomer);
    /* If the customer's services are not private owned then use a     ReadObjectQuery to refresh all parts */
    ReadObjectQuery query = new ReadObjectQuery(customer);
    /* Refresh the cache with the query's result and cascade refreshing 
      to all parts including customer's services */
    query.refreshIdentityMapResult();
    query.cascadeAllParts();
    /* Refresh from the database */
    query.dontCheckCache();
    session.executeQuery(query);
    /* Retry… */
}

When is an Object Considered Changed?

The Unit of Work considers an object changed when you modify its direct-to-field or aggregate object mapping attribute. Adding, removing, or modifying objects related to the source object does not render the source object changed for the purposes of the Unit of Work.

Pessimistic Locking

Pessimistic locking locks objects when the transaction accesses them, before commit time, ensuring that only one client is editing the object at any given time.

Pessimistic locking detects locking violations at object read time. The OracleAS TopLink implementation of pessimistic locking uses database row-level locks, such that attempts to read a locked row either fail or are blocked until the row is unlocked, depending on the database.

Example 5-19 Pessimistic Locking with ReadObjectQuery

import oracle.toplink.sessions.*;
import oracle.toplink.queryframework.*;

...
UnitOfWork uow = session.acquireUnitOfWork();

ReadObjectQuery query = new ReadObjectQuery();
query.setReferenceClass(Employee.class);
query.acquireLocks(); 
Employee employee = (Employee) uow.executeQuery(query);

// Make changes to object
...

uow.commit();
...

Example 5-20 Pessimistic Locking with ReadAllQuery

import oracle.toplink.sessions.*;
import oracle.toplink.queryframework.*;

...
UnitOfWork uow = session.acquireUnitOfWork();
ReadAllQuery query = new ReadAllQuery();
query.setReferenceClass(Employee.class);
query.setSelectionCriteria(new ExpressionBuilder().get("salary").greaterThan(25000));
query.acquireLocks(); 
/* NOTE: the objects are registered when they are obtained by using Unit of Work. OracleAS TopLink will update all the changes to registered objects when Unit of Work commit */
Vector employees = (Vector) uow.executeQuery(query);
    // Make changes to objects
    ...
    uow.commit();
...

Example 5-21 Pessimistic Locking with a Session Using ReadAllQuery

import oracle.toplink.sessions.*;
import oracle.toplink.sessions.queryframework.*;
...
// It must begin a transaction or the lock request will throw an exception
session.beginTransaction();
ReadAllQuery query = new ReadAllQuery();
query.setReferenceClass(Employee.class);
query.setSelectionCriteria(new ExpressionBuilder().get("salary").greaterThan(25000));
query.acquireLocks(); 
// or acquireLocksWithoutWaiting()
query.refreshIdentityMapResult();
Vector employees = (Vector) session.executeQuery(query);
// Make changes to objects
...
// Update objects to reflect changes
for (Enumeration enum = employees.elements(); 
    employees.hasMoreElements(); {
    session.updateObject(enum.nextElement());
}
session.commitTransaction();
...

Pessimistic Locking and the Cache

When you acquire a pessimistic lock on an object, you refresh the object in the session cache. This is different from an optimistic lock, which refreshes objects in the cache only after a successful commit. Because of this, and because it prevents other processes from reading locked objects, a pessimistic lock is not as efficient as an optimistic lock.


Note:

OracleAS TopLink uses database row-level locking to implement pessimistic locking. Although this is the standard way of implementing pessimistic locking in the database, not all databases support row-level locking functionality. Consult your database documentation to see if your database supports row-level locking and the SELECT ... FOR UPDATE [NO WAIT] API.

Pessimistic Locking and Database Transactions

Because pessimistic locks exist for the duration of the current transaction, the associated database transaction remains open from the point of the first lock request until the transaction commits. When the transaction commits or rolls back, the database releases the locks.

The Unit of Work starts a database transaction automatically when it attempts to read the first object in its operations. If you are not using the Unit of Work, manually begin a transaction on the session.

WAIT and NO_WAIT Options

OracleAS TopLink offers two methods of locking, WAIT and NO_WAIT. These options determine how the transaction responds when it encounters a locked row. If you select:

  • The WAIT option, then the transaction waits until the database releases the lock on the object. It then obtains a lock on the object and continues.

  • The NO_WAIT option, then OracleAS TopLink throws an exception when the transaction encounters a locked row.

Example 5-22 Pessimistic Locking with Wait for Lock

This example illustrates a pessimistic lock with the WAIT mode in the context of a Unit of Work.

import oracle.toplink.sessions.*;
import oracle.toplink.queryframework.*;
...
UnitOfWork uow = session.acquireUnitOfWork();
Employee employee = (Employee) uow.readObject(Employee.class);

/* Note: This will cause the Unit of Work to begin a transaction. In a three-Tier model this will also cause the ClientSession to acquire its write connection from the ServerSession's pool */
uow.refreshAndLockObject(employee, ObjectLevelReadQuery.LOCK);
// Make changes to object
...
uow.commit();
...

Example 5-23 Pessimistic Locking with No Wait for Lock

This example illustrates a pessimistic lock with the No_Wait mode in the context of a Unit of Work.

import oracle.toplink.sessions.*;
import oracle.toplink.queryframework.*;
import oracle.toplink.exceptions.*;
...
UnitOfWork uow = session.acquireUnitOfWork();
Employee employee = (Employee) uow.readObject(Employee.class);

try {
    employee = (Employee)
      uow.refreshAndLockObject(employee,ObjectLevelReadQuery.LOCK_NOWAIT);
} 
catch (DatabaseException dbe) {
    // Some databases throw an exception instead of returning nothing.
    employee = null;
}
if (employee == null) {
    // Lock cannot be obtained
    uow.release();
    throw new Exception("Locking error.");
} else {
    // Make changes to object
    ...
    uow.commit();
}
...

Advantages of Pessimistic Locking

The following are the advantages of pessimistic locking:

  • It prevents users and applications from editing data that is being or has been changed.

  • Processes know immediately when a locking violation occurs, rather than after the transaction is complete.

Disadvantages of Pessimistic Locking

The following are the disadvantages of pessimistic locking:

  • It is not fully supported by all databases.

  • It consumes extra database resources.

  • It requires OracleAS TopLink to maintain an open transaction and database lock for the duration of the transaction, which can lead to database deadlocks.

  • It decreases the concurrency of connection pooling when using the server session, which affects the overall scalability of your application.

Reference

Table 5-2 summarizes the most common public methods for Pessimistic Locking. The Default column describes default settings of the descriptor element. For more information about the available methods for Pessimistic Locking, see the Oracle Application Server TopLink API Reference.

Table 5-2 Elements for Pessimistic Locking

Element Default Method Name
Lock mode (for ObjectLevelRead Query) No lock
acquireLocks()
acquireLocksWithoutWaiting()
Refresh and lock (for Session) not applicable
refreshAndLockObject(Object object, short lockMode)

Two Different Locking Policies

A locking policy describes how you manage record locking on the database and track changed objects. OracleAS TopLink offers two different strategies for managing locking: field locking and timestamp locking.

Field Locking Policies

Field locking policies compare the current values of certain mapped fields with previous values. OracleAS TopLink support for field locking policies does not require any additional fields in the database. Field locking policy support includes:

  • AllFieldsLockingPolicy

  • ChangedFieldsLockingPolicy

  • SelectedFieldsLockingPolicy

These policies require you to use a Unit of Work for database updates. Each policy handles its field comparisons in a specific way defined by the policy:

  • When you update or delete an object under AllFieldsLockingPolicy, the Unit of Work checks all table fields that are part of the SQL where clause. If any values have changed since the object was read, the update or delete fails. This comparison is only on a per table basis. If you perform an update on an object mapped to multiple tables (including multiple table inheritance), only the changed tables appear in the where clause.

  • When you update an object under ChangedFieldsLockingPolicy, the Unit of Work checks only the modified fields. This allows multiple clients to modify different parts of the same row without failure. Using this policy, a delete compares only on the primary key.

  • When you update or delete an object under SelectedFieldsLockingPolicy, the Unit of Work compares a list of selected fields in the update statement.

When an update fails due to an optimistic locking violation, OracleAS TopLink raises an OptimisticLockException. Under most circumstances, the application handles this exception by refreshing the object and reapplying changes.

Version Locking Policies

OracleAS TopLink supports version locking policies through the VersionLockingPolicy interface and the TimestampLockingPolicy interface. Each of these policies requires an additional field in the database to operate:

  • For VersionLockingPolicy, add a numeric field to the database.

  • For TimestampLockingPolicy, add a timestamp field to the database.

OracleAS TopLink records the version as it reads an object from a table. When the client attempts to write the object, OracleAS TopLink compares the object version with the version in the table record. If the versions match, OracleAS TopLink writes the updated object to the table and updates the version of both the table record and the object. If the versions are different, the write fails and OracleAS TopLink raises an error.

These two version locking policies have different ways of writing the version fields back to the database:

  • VersionLockingPolicy increments the value in the version field by one.

  • TimestampLockingPolicy inserts a new timestamp into the row. The timestamp is configurable to get the time from the server or the local system.

For either policy, you write the value of the write lock field in either the identity map or in a writable mapping within the object.

If you store the value in the identity map, you do not require an attribute mapping for the version field. However, if the application does map the field, the mappings must be read-only to allow OracleAS TopLink to control writing the fields.

Timestamp Versus Version Locking Policies

When choosing a locking policy, note the following:

  • If you need absolute certainty for versioning, and especially if your database does not offer fine time granularity, implement the VersionLockingPolicy. This policy uses integers for field locking and guarantees that you recognize changes.

  • If your database time offers a fine granularity, or if you need to know when an object was last updated, implement the TimestampLockingPolicy.