Skip Headers

Oracle TopLink Developer's Guide 10g Release 3 (10.1.3) B13593-01 |
| Home | Solution Area | Contents | Index

Previous | Next |

# Using Optimistic Read Locking with forceUpdateToVersionField

If your descriptors are configured to use an optimistic version locking policy (see "Optimistic Version Locking Policies") or field locking policy (see "Optimistic Field Locking Policies"), use the unit of work method `forceUpdateToVersionField` to solve either or both of the following problems:

- You want an `OptimisticLockingException` thrown at commit time if an object you read in a transaction has changed since it was registered even though you have not changed the object in your transaction (see "Forcing a Check of the Optimistic Read Lock").

- You modify an object in a transaction in such a way that its version field is not updated but you want to alert other threads of the change by way of the version field (see "Forcing a Version Field Update").

  For example, you change a privately owned object that has its own database table so the parent object's version field is not, by default, updated. In this case, you can use `forceUpdateToVersionField` to update the parent's version field.

  As an alternative to this approach, consider "Optimistic Version Locking Policies and Cascading".

To remove `forceUpdateToVersionField` configuration from an object before a commit operation, use the unit of work method `removeForceUpdateToVersionField` (see "Disabling forceUpdateToVersionField").

## Forcing a Check of the Optimistic Read Lock

When you read an object with the unit of work, optimistic lock checking is not applied to that object at commit time unless you change the object. However, there are times when you want your transaction to fail if the state of an object has changed since it was read, even though you have not modified the object.

Example 102-12 shows a transaction that updates a mortgage rate by multiplying the central bank prime rate by 1.25. The transaction forces 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 102-12 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… */
    }

For another example that forces both optimistic locking and a version field update, see Example 102-13 in "Forcing a Version Field Update".

## Forcing a Version Field Update

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. In other words, when a relationship is changed in a one-to-many or one-to-one target foreign key mapping, by default, the version field (if any) of the affected object is not changed.

If you configure a descriptor to refresh the cache only if the database version is newer than the cache version (using descriptor method `onlyRefreshCacheIfNewerVersion`), and such a relationship changes, you will not be able to refresh the object at all. Because the version has not changed, the unit of work method `refreshObject` and even a query with `refreshIdentityMapResults` option set to `true` cannot refresh the object.

Using the unit of work method`forceUpdateToVersionField` passing in both the unit of work copy clone and `true` value will ensure that the object's version field is updated when such a change is made. It will also ensure that changes to the object before it is refreshed will result in optimistic locking exceptions, preventing the writing of stale data (see "Forcing a Check of the Optimistic Read Lock").

Example 102-13 and Example 102-14 show transactions executing in separate threads that access the same customer object concurrently. The unit of work method forceUpdateToVersionField is used to ensure that changes to the customer object in one thread are detected by the other threads.

Example 102-13 shows a transaction in which an invoice thread calculates an invoice for a customer. Example 102-14 shows a transaction in which another thread, the service thread, adds a service to the same customer or modifies the current service. In either case, the service thread must inform the invoice thread, which adds the changes to the invoice.

Example 102-13 Optimistic Read Lock with Version Increment: Service Thread

    /* 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… */
    }

Example 102-14 Optimistic Read Lock with Version Increment: Invoice Thread

    /* 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 service 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 privately 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… */
    }

## Disabling forceUpdateToVersionField

The `forceUpdateToVersionField` configuration you apply to an object stays in effect for the lifetime of your unit of work. After you commit your transaction, `forceUpdateToVersionField` configuration no longer applies.

To remove `forceUpdateToVersionField` configuration from an object before commit time, use the unit of work method `removeForceUpdateToVersionField`. TopLink will not apply optimistic read locking to the object unless you change it in this transaction (that is, unless you modify its direct-to-field or aggregate object mapping attribute).