|
Using Database LockingA J2EE Application Server only provides locking mechanisms for Entity EJBs. Many J2EE systems do not use EJBs at all or only use Session EJBs and perform all of their concurrency control using the mechanisms built into the underlying database system. This page describes the different approaches taken by different database vendors and shows how to use them from Java. JDBC Isolation LevelMost database systems operate by default with a Read-Committed isolation level. If an application requires a higher level of isolation, then it can change isolation level in a portable manner using the setTransactionIsolation call on a normal JDBC Connection; for example:
import java.sql.Connection;
Connection c = getConnection();
c.setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE);
Most database systems do not allow the isolation level of a connection to be modified when a transaction is in progress and so the Configuring Isolation Level for a DataSourceThe mechanisms used to set the isolation level for a DataSource vary from vendor to vendor. For the JBoss server, it can be defined as a property in the -ds.xml file:
This will cause the connection pool to set the isolation level the first time the connection is returned during a transaction. Avoiding DeadlocksUnfortunately simply setting the isolation level for the transaction is not sufficient to avoid the data concurrency issue. By raising the isolation level above Read-Committed, increase the potential for a deadlock when two overlapping transactions try to update the same data at the same time. It is important to realize that this deadlock can occur even if you take the care to access data in a consistent order. For example, consider the following scenario:
To avoid this problem, database systems allow you to specify in a read operation that you are reading data with the intent to update it later in the same transaction. The ISO standard syntax for this is to use a FOR UPDATE cursor: DECLARE c CURSOR FOR SELECT NAME FROM PERSON WHERE ID = 123 FOR UPDATE This will typically cause the database to take exclusive locks on the rows identified by the query when they are read rather than waiting until the data is updated. Hence the scenario above becomes:
Because T1 requested the lock on read, the deadlock was avoided. The standard JDBC way of specifying this is to use an updatable ResultSet, for example:
ps = conn.createStatement(
"SELECT NAME FROM PERSON WHERE ID=?",
ResultSet.TYPE_FORWARD_ONLY,
ResultSet.CONCUR_UPDATABLE
);
which causes the driver to generate the required SQL. However, not all JDBC driver implementations support this functionality (it is an optional feature of the specification), or support it in the same manner. Many databases have adopted a "SELECT FOR UPDATE" syntax where the for-update clause can be added to a normal SELECT statement, for example:
ps = conn.createStatement(
"SELECT NAME FROM PERSON WHERE ID=? FOR UPDATE OF NAME"
);
This version uses the optional OF clause to indicate precisely which columns are going to be updated which is useful if the query uses multiple tables as exclusive locks are only taken on the tables specified; if this clause is omitted then the database will lock all the rows used in all tables in the FROM clause. Microsoft SQL Server 2000 uses a different approach where you specify the type of lock to take on a table in the FROM clause, for example:
ps = conn.createStatement(
"SELECT NAME FROM PERSON WITH (UPDLOCK) WHERE ID=?"
);
When this query is executed, the database takes an update lock on the selected rows rather than the normal shared read lock. This allows other readers to continue to read the data but all other writers will block; when you actually perform the update, then the lock will be escalated to an exclusive lock until you commit. Commit FailuresSome database systems avoid the deadlock issue described above by versioning the data used during a transaction. Although this eliminates the potential for the deadlock, it may introduce the possibility of other failures. The Oracle database, for example, uses a multi-versioning system that eliminates the need for readers to obtain locks to ensure read-repeatability. However, this can lead to other potential failures and transaction rollback:
A similar multi-versioning system is used by the InnoDB engine of MySQL. Another system that takes this even futher is the fully multi-versioned McKoi database. McKoi never locks data, even on write, but detects serialization problems at commit time and rolls back transactions that do not meet the serialization criteria. This provides high performance in low contention situations, but at the expense of having to rollback and have the application redo the work if a concurrency violation occurs. |
|
||||||
© 2003 Core Developers Network Ltd "Core Developers Network", the stylized apple logo and "Core Associates" are trademarks of Core Developers Network Ltd. All other trademarks are held by their respective owners. Core Developers Network Ltd is not affiliated with any of the respective trademark owners. |