Handling versioning and concurrency

For any multiuser transactional system, you must decide between optimistic and pessimistic concurrency to handle concurrent updates and versioning issues. In this recipe, I'll show you how to properly set up versioning and optimistic concurrency with NHibernate.

Getting ready

Complete all the previous recipes including Setting up a base entity class.

How to do it...

  1. In the Entity base class, add a Version property, as shown in the following code:
    public abstract class Entity<TId>
    {
    
      public virtual TId Id { get; protected set; }
      protected virtual int Version { get; set; }
    
      public override bool Equals(object obj)
      {
        return Equals(obj as Entity<TId>);
      }
  2. In the Product mapping, add the version element as shown in the following code:
    <natural-id mutable="true">
      <property name="Name" not-null="true" />
    </natural-id>
    <version name="Version" />
    <property name="Description" />
    <property name="UnitPrice" not-null="true" />
  3. In the ActorRole mapping, add the version element shown here:
    <id name="Id">
      <generator class="guid.comb" />
    </id>
    <version name="Version" />
    <property name="Actor" not-null="true" />
    <property name="Role" not-null="true" />

How it works...

Suppose you have a database application with two users. User #1 and user #2 both pull up the same data on their screen and begin making changes. User #1 submits her changes back to the database. A few moments later, user #2 submits his changes. Without any concurrency checking, user #2's changes will silently overwrite user #1's changes. There are two possible ways to prevent this: optimistic and pessimistic concurrency.

Optimistic concurrency is the process where data is checked for changes before any update is executed. In this scenario, user #1 and user #2 both begin their changes. User #1 submits her changes. When user #2 submits his changes, his update will fail because the current data (after user #1's changes) doesn't match the data that user #2 originally read from the database.

In the example shown here, we use the version field to track changes to an entity. Update statements takes the following form:

UPDATE Product
SET    Version = 2 /* @p0 */,
       Name = 'Junk' /* @p1 */,
       Description = 'Cool' /* @p2 */,
       UnitPrice = 100 /* @p3 */
WHERE  Id = '764de11e-1fd0-491e-8158-9db8015f9be5' /* @p4 */
       AND Version = 1 /* @p5 */

NHibernate checks that the version is the same value as when the entity was loaded from the database, and then increments the value. If the entity was already updated, the version field will not be 1, and no rows will be updated by this statement. NHibernate detects the zero rows affected and throws a StaleStateException, meaning the entity in memory is stale, or out of sync with the database.

There's more...

The alternative to optimistic concurrency is pessimistic locking. Pessimistic locking is the process where a user obtains an exclusive lock on the data while they are editing it. It takes the pessimistic view that, given the chance, user #2 will overwrite user #1's changes, so it's best not to let user #2 even look at the data. In this scenario, once user #1 pulls up the data, she has an exclusive lock. User #2 will not be able to read that data. His query will wait until user #1 drops the lock or the query times out. Inevitably, user #1 will take a phone call or step away for a cup of coffee while user #2 waits for access to the data. To implement this type of locking with NHibernate, your application must call session.Lock within a transaction.

Other methods of optimistic concurrency

In addition to integer version fields, NHibernate also allows you to use DateTime-based version fields. However, Micorosoft SQL Server has a datetime resolution of about three milliseconds. This may fail when two updates occur almost simultaneously. It's also possible to use SQL Server 2008's DateTime2 data type, which has a resolution of 100 nanoseconds, or even SQL Server's timestamp data type for the version field.

NHibernate allows you to use the more traditional form of optimistic concurrency through the mapping attribute optimistic-lock. A simple example would look like the following code:

<class name="Product" 
       dynamic-update="true" 
       optimistic-lock="dirty">

In this case, changing a Product name from Stuff to Junk would generate SQL as shown in the following code:

UPDATE Product
SET    Name = 'Junk' /* @p0 */
WHERE  Id = '741bd189-78b5-400c-97bd-9db80159ef79' /* @p1 */
       AND Name = 'Stuff' /* @p2 */

This ensures that the Name value hasn't been changed by another user because this user read the value. Another user may have changed other properties of this entity.

Another alternative is to set optimistic-lock to all. In this case, a Product update would generate SQL like this:

UPDATE Product
SET    Name = 'Junk' /* @p0 */
WHERE  Id = 'd3458d6e-fa28-4dcb-9130-9db8015cc5bb' /* @p1 */
       AND Name = 'Stuff' /* @p2 */
       AND Description = 'Cool' /* @p3 */
       AND UnitPrice = 100 /* @p4 */

As you might have guessed, in this case, we check the values of all properties.

When optimistic-lock is set to dirty, dynamic-update must be true. Dynamic update simply means that the update statement only updates dirty properties, or properties with changed values, instead of explicitly setting all properties.

See also

  • Mapping a class with XML
  • Creating class hierarchy mappings
  • Mapping a one-to-many relationship
  • Setting up a base entity class
  • Creating mappings fluently
  • Mapping with ConfORM
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.147.44.182