Oracle TopLink Developer's Guide
10g Release 3 (10.1.3) B13593-01 |
|
![]() Previous |
![]() Next |
This section describes the various types of locking policy that TopLink supports, including the following:
For more information, see "Configuring Locking Policy".
With optimistic locking, all users have read access to the data. When a user attempts to make a change, the application checks to ensure the data has not changed since the user read the data.
Optimistic version locking policies enforce optimistic locking by using a version field (also known as a write-lock field) that you provide in the reference class that TopLink updates each time an object change is committed.
TopLink caches the value of this version field as it reads an object from the data source. When the client attempts to write the object, TopLink compares the cached version value with the current version value in the data source in the following way:
If the values are the same, TopLink updates the version field in the object and commits the changes to the data source.
If the values are different, the write operation is disallowed because another client must have updated the object since this client initially read it.
TopLink provides the following version-based optimistic locking policies:
VersionLockingPolicy
: requires a numeric version field; TopLink updates the version field by incrementing its value by one.
TimestampLockingPolicy
: requires a timestamp version field; TopLink updates the version field by inserting a new timestamp (this policy can be configured to get the time from the data source or locally; by default, the policy gets the time from the data source).
Note: In general, Oracle recommends numeric version locking, because:
|
Whenever any update fails because optimistic locking has been violated, TopLink throws an OptimisticLockException
. This should be handled by the application when performing any database modification. The application must notify the client of the locking contention, refresh the object, and have the client reapply its changes.
You can choose to store the version value in the object as a mapped attribute, or in the cache. In three-tier applications, you typically store the version value in the object to ensure it is passed to the client when updated (see "Locking in a Three-Tier Application").
If you store the version value in the cache, you do not need to map it. If you do map the version field, you must configure the mapping as read-only (see "Configuring Read-Only Mappings").
To ensure that the parent object's version field is updated whenever a privately owned child object is modified, consider "Optimistic Version Locking Policies and Cascading".
When using optimistic version locking with the unit of work, consider "Using Optimistic Read Locking with forceUpdateToVersionField".
If your database schema is such that both a parent object and its privately owned child object are stored in the same table, then if you update the child object, the parent object's version field will be updated.
However, if the parent and its privately owned child are stored in separate tables, then changing the child will not, by default, update the parent's version field.
To ensure that the parent object's version field is updated in this case, you can either manually update the parent object's version field (see "Using Optimistic Read Locking with forceUpdateToVersionField") or, if you are using a TimestampLockingPolicy
, you can configure TopLink to automatically cascade the child object's version field update to the parent (see "Configuring Optimistic Locking Policy Cascading").
After you enable optimistic version locking cascading, when a privately owned child object is modfied, TopLink will traverse the privately owned foreign reference mappings, updating all the parent objects back to the root.
Optimistic version locking cascading is only applied if the child object is registered in a unit of work.
TopLink supports optimistic version locking cascading for:
object changes in privately owned one-to-one and one-to-many mappings
relationship changes (adding or removing) in the following collection mappings (privately owned or not):
direct collection
one-to-many
many-to-many
aggregate collection
Consider the example object graph shown in Figure 26-5
Figure 26-5 Optimistic Version Locking Policies and Cascading Example
In this example, ObjectA
privately owns ObjectB
, and ObjectB
privately owns ObjectC
, and ObjectC
privately owns ObjectD
.
Suppose you register ObjectB
in a unit of work, modify an ObjectB
field, and commit the unit of work. In this case, ObjectB
checks the cache for ObjectA
and, if not present, queries the database for ObjectA
. ObjectB
then notifies ObjectA
of its change. ObjectA
forces an update on its version optimistic locking field even though it has no changes to its corresponding table.
Suppose you register ObjectA
in a unit of work, access its ObjectB
to access its ObjectC
to access its ObjectD
, modify an ObjectD
field, and commit the unit of work. In this case, ObjectD
notifies ObjectC
of its changes. ObjectC
forces an update on its version optimistic locking field even though it has no changes to its corresponding table. ObjectC
then notifies ObjectB
of the ObjectD
change. ObjectB
then notifies ObjectA
of the ObjectD
change. ObjectA
forces an update on its version optimistic locking field even though it has no changes to its corresponding table.
With optimistic locking, use the UnitOfWork
method commitAndResumeOnFailure
(see "Resuming a Unit of Work After Commit") to rollback a locked object's value, if you store the optimistic lock versions in the cache.
If you store the locked versions in an object, you must refresh the objects (or their versions) on a failure. Alternatively, you can acquire a new unit of work on the failure and reapply any changes into the new unit of work.
Optimistic field locking policies enforce optimistic locking by using one or more of the fields that currently exist in the table to determine if the object has changed since the client read the object.
The unit of work caches the original state of the object when you first read the object or register it with the unit of work. At commit time, the unit of work compares the original values of the lock fields with their current values on the data source during the update. If any of the lock field's values have changed, an optimistic lock exception is thrown.
TopLink provides the following optimistic field locking policies:
AllFieldsLockingPolicy
: For update and delete operations, TopLink compares all the fields of the object with all the fields in the data source. If the original value of any fields differ from that in the data source, the write operation is disallowed.
For example, if you changed a customer's last name, TopLink might produce SQL like:
UPDATE CUSTOMER SET LNAME='new last name' WHERE ID=7 AND LNAME='old last name' AND FNAME='Donald' AND B_DAY='1972' AND CREDIT_RATING='A+' AND EYE_COLOR='Blue'
The main disadvantage of this field locking policy is that it is not the most efficient, especially if the changed object has many attributes.
Note: This comparison is only on a per table basis. If an update operation is performed on an object that is mapped to multiple tables (multiple table inheritance), then only the changed fields for each table changed appear in thewhere clause.
|
ChangedFieldsLockingPolicy
: For update operations, TopLink compares only the fields of the object that have changed with the corresponding fields in the data source. If the original value of any such field differs from that in the data source, the write operation is disallowed. TopLink does not make any field comparisons for deletes.
The main advantage of this field locking policy is that it allows concurrent updates of different fields. For example, if one thread updates a customer's last name and another thread updates the same customer's credit rating, and you configure the Customer
descriptor with ChangedFieldsLockingPolicy
, then TopLink might produce SQL like:
// Unit of Work 1 UPDATE CUSTOMER SET LNAME='new name' WHERE ID=7 AND LNAME='old name' // Unit of Work 2 UPDATE CUSTOMER SET CREDIT_RATING='B' WHERE ID=7 AND CREDIT_RATING='A+'
SelectedFieldsLockingPolicy
: For update and delete operations, TopLink compares only the selected fields of the object with the corresponding fields in the data source. If the cached value of any such field differs from that in the data source, the write operation is disallowed.
For example, if you select Customer
attributes LNAME
and CREDIT_RATING
, then at run time, TopLink might produce SQL like:
UPDATE CUSTOMER SET LNAME='new name' WHERE ID=7 AND LNAME='old name' AND CREDIT_RATING='A+'
Whenever any update fails because optimistic locking has been violated, TopLink throws an OptimisticLockException
. This should be handled by the application when performing any database modification. The application must notify the client of the locking contention, refresh the object, and have the client reapply its changes.
When using field locking policies, a unit of work must be employed for updating the data source.
Note: You cannot use an instance ofFieldsLockingPolicy if you are using AttributeChangeTrackingPolicy (see "Attribute Change Tracking Policy").
|
With pessimistic locking, the first user who accesses the data with the purpose of updating it locks the data until completing the update.
When using a pessimistic locking policy, you can configure the policy to either fail immediately or to wait until the read lock is acquired.
You can use a pessimistic locking policy only in a project with a persistence type of CMP (see "Configuring Persistence Type") and with descriptors that have EJB information (see "Configuring a Descriptor With EJB Information").
You can also use pessimistic locking (but not a pessimistic locking policy) at the query level (see "Configuring Named Query Options").
If you are building a three-tier application, in order to correctly lock an object, you must obtain the lock before the object is sent to client for editing.
If you are using optimistic locking, you have two choices for locking objects correctly:
Map the optimistic lock field in your object as not read-only and pass the version to the client on the read and back to the server on the update.
You must define a non-read-only mapping for the version field and make the optimistic locking policy store the version value in the object, not the cache (in TopLink Workbench, this is done on the Locking tab by unchecking Store Version in Cache: see "Using TopLink Workbench").
Ensure that the original version value is sent to the client when it reads the object for the update. The client must then pass the original version value back with the update information, and this version must be set into the object to be updated after it is registered/read in the new unit of work on the server.
Hold the unit of work for the duration of the interaction with the client.
Either through a stateful session bean, or in an HTTP session, store the unit of work used to read the object for the update for the duration of the client interaction.
Your must read the object through this unit of work before passing it to the client for the update. This ensures that the version value stored in the unit of work cache or in the unit of work clone will be the original value.
This same unit of work must be used for the update.
The first option is more commonly used, and is required if developing a stateless application.
If you are using pessimistic locking, you must use the unit of work to start a database transaction before the object is read. You must hold this unit of work and database transaction while the client is editing the object and until the client updates the object. You must use this same unit of work to update the object. If you are building a three-tier Web application (where it is not normally desirable to hold a database transaction open across client interactions), optimistic locking is normally more desirable than pessimistic locking (see "Optimistic Locking in a Three-Tier Application").