A multi-user application must control concurrency, the simultaneous access of the same data by many users. Otherwise, data could be updated or changed improperly. Concurrency control is managed through the Transaction interface. This topic describes how transactions fulfill the following important requirements of an information management system:
Data must be read and modified in a consistent fashion.
Data concurrency of a multi-user system must be maximized.
High performance is required for maximum productivity from clients.
Transactions use locks to control concurrent access to data, achieving two important database goals:
Consistency ensures that the data you are viewing or changing is not changed by other users until you are finished with the data.
Integrity ensures that the database data and structures reflect all changes made to them in the correct sequence.
The Business Components for Java framework supports a locking model that provides the following features and benefits:
A lock is placed even if inconsistency is detected.
It can determine that inconsistency is due to DELETE or UPDATE of data.
It supports middle-tier server comparison of original data columns.
Data is retrieved to correct inconsistencies when they occur, with no further roundtrips required.
Using the Business Components for Java framework, you can request locks on rows in entity objects or view objects. Locking an entity object row places a database lock on that row. When locking a view object's row, the framework requests locks on those Entity rows that are used in the view object's row.
Business Components for Java supports the locking styles listed in the table below. Each style represents a trade-off between scalability, performance, and consistency of data. For example, placing database locks early (pessimistic locking) reduces scalability but decreases the likelihood of data inconsistencies. When you change the locking style, the change only affects subsequent locks. Locks already in place do not change.
Locking Style |
Comments |
Pessimistic Locking |
Locks are automatically placed upon the underlying row immediately before the first client change is made. This is the default style for Application Modules. Represented by the constant Transaction.LOCK_PESSIMISTIC. |
Optimistic Locking |
Locks are automatically placed upon the underlying row during the "save to database" logic. Represented by the constant Transaction.LOCK_OPTIMISTIC. |
Explicit Locking |
Locks are manually placed, at the correct point in time, by explicit calls from the client. If no lock calls occur, then only the database locks, obtained when the rows are flushed to the server, are obtained. Represented by the constant Transaction.LOCK_NONE. |
The following code example demonstrates pessimistic locking mode:
if ( myEntity.getPostState() != Entity.STATUS_NEW
&& myEntity.getDBTransaction().getLockingMode()
== Transaction.LOCK_PESSIMISTIC
&& !myEntity.isLocked() )
{
myEntity.lock();
}
return myEntity;
Locks may fail for any of the following reasons:
The resource is held by another user.
The resource has been deleted from the Database.
The resource changed since the entity object cache was populated.
Some general SQL error condition (such as the database going down).
In each case, a different type of error is raised to indicate the problem. This permits higher level callers to indicate the error in question, and potentially retry the operation.
After a lock is placed on a given object, it is added to the list of Transaction participants. After the Transaction performs a commit or rollback operation, each Transaction participant is notified of the outcome of the operation. Each object is given an opportunity to release any internal state, such as lock indicator flags, that it may hold during this notification cycle.
Independent of the locking style selected, the low-level APIs permit the explicit locking of View/Entity Rows by calling their lock method at the appropriate time.
After the initial lock call for a given row object, subsequent calls are ignored unless the corresponding database transaction has had the commit or rollback operation performed, because locks are automatically released at these times.
The call to place a lock can fail for a number of reasons:
Resource held by another user.
Resource deleted from the database.
Resource changed since the entity object cache was populated.
General SQL Error condition (such as the database going down).
In each case, an exception is thrown to indicate that a problem occurred. These exceptions can also occur at other times, such as changing the value of a column, because locks can be placed implicitly at that time.
An Entity Row provides a method for testing the locked status, as follows:
/**
* Is the Row locked?
**/
public boolean isLocked()