Skip Headers

# Forcing a Version Field Update

Previous

Next

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: Optimistic Read Lock with Version Increment: Service Thread and Example: Optimistic Read Lock with Version Increment: Invoice Thread 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: Optimistic Read Lock with Version Increment: Service Thread shows a transaction in which an invoice thread calculates an invoice for a customer. Example: Optimistic Read Lock with Version Increment: Invoice Thread 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.

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

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