Optimistic Locking is a typical concept for the management of concurrent
update access from multiple applications on one database. An update of an
existing record is only performed if it has not been modified by someone
else since the current application has read the record of interest the last
time. If it was modified, the caller is informed about a concurrent access
conflict. This concept can easily be implemented with PriDE making use of
the patterns introduced so far. All entity types which optimistic locking
should be applied to, inherit from a base class, providing the required locking
information. This is usually just a simple version counter for every record,
e.g.
class OptimisticLock extends MappedObject { protected static RecordDescriptor red = new RecordDescriptor (OptimisticLock.class, null, null, new String[][] { { "version", "getVersion", "setVersion" }, }); protected RecordDescriptor getDescriptor() { return red; } private long version; protected OptimisticLock() { version = 0; } public int update() throws SQLException { // Lock check goes here... super.update();
|
For the detection of access conflicts there are two different approaches
explained below. The more efficient one concerning the number of database
operations increments the version counter with every update call. The non-incremented
value is added to the where-clause causing the update to work only if the
record's version counter wasn't modified by another application in the meanwhile.
A simple implementation is an extension for the where-clause which PriDE
uses for a database update:
class OptimisticLock extends MappedObject { //... public int update() throws SQLException {
|
The application assumes a conflict, if the update returns 0, i.e. there
was no record found matching the restriction by primary key and version number.
Another concept is to read the version counter immediatly before update
in a way that the corresponding record is locked for concurrent write access
by different applications (select-for-update). The actual update is
performed only if the entity's state and the record's state in the database
are identical. This concept takes additional database operations and is therefore
less efficient. On the other hand it is much safer as it can also become
aware of a concurrent record deletion (both physical and logical). As a
difference to the standard read method, the entity's current data must not
be overridden in the select-for-update to keep the state which is required
for later update. Reading only the version counter reduces the data transfer
between application and database. The following class provides the corresponding
base functionality:
public class OptimisticLockClone extends OptimisticLock {
// The object to update and thus the one to take // the primary key data for a version number query from private OptimisticLock source; // Specialized record descriptor, which takes
data for a query from member // Always take the
table name from the 'source' object above // Always build constraints
from the 'source' object above // Always query for
the 'version' field only private RecordDescriptor red = new CloneDescriptor();
public OptimisticLockClone(OptimisticLock source) { this.source = source; } public void findAndLock() throws SQLException
{ |
By means of class OptimisticLockClone the function OptimisticLock.update()
can now be extended by the version check as follows:
class OptimisticLock extends MappedObject { //... public int update() throws SQLException {
|
The complete source code of the examples above can be found in examples/locking.
Home | Introduction | Javadoc |