Optimistic locking

A <cftransaction> could prevent scalability in highly concurrent applications because it locks a database for a transaction that could run for a longer time. Also, a transaction cannot run beyond the duration of a request. There can be scenarios where an object is loaded in one request and the same instance is updated in another request or by another application. In this scenario, the application needs to maintain the transaction semantics and prevent the update if the row has been modified by some other request. This can be achieved by using optimistic concurrency control, which allows high concurrency in your application along with high scalability.

Optimistic concurrency control uses either number-based or timestamp-based versioning approach. In a number-based approach, a version number is incremented and for the timestamp approach, a timestamp is set to the current time whenever the object is modified. It must be noted that version increment or timestamp updation is managed by Hibernate and is not triggered at the database level.

  • Using version: To use optimistic concurrency control using version numbers, add a property with fieldtype='version' in your CFC.

    For example:

    /** 
    * @persistent 
    * @table Users 
    */ 
    component{ 
        property name="id" fieldtype="id" datatype="int" generator="native"; 
        property string fname; 
        property string lname; 
        property name="version" fieldtype="version" datatype="int" ; 
    }

    Whenever a user object is updated, its version number is automatically incremented. The version number is used in the SQL update statement in such a way that updating proceeds only when the version number has not been changed by some other request or some other application.

    In case updating fails because the version number was changed outside the current session, an error is thrown specifying that the session contained stale data.

  • Using timestamp: To use optimistic concurrency control using timestamp, add a property with fieldtype="timestamp" in your CFC.

    For example:

    /** 
    * @persistent 
    * @table Users 
    */ 
    component{ 
        property name="id" fieldtype="id" datatype="int" generator="native"; 
        property string fname; 
        property string lname; 
        property name="lastModified" fieldtype="timestamp"; 
    }

    Whenever a user object is updated, its timestamp is automatically set to the current time. Sometimes this is preferred over version because it also tells you when the user object was last modified.

    In case updating fails because the timestamp was changed outside of the current session, an error is thrown specifying that the session contained stale data.

If you do not have version or timestamp properties in your object, it is still possible to use optimistic locking, but only for objects that are retrieved and modified in the same ORM session. For optimistic locking of detached objects (objects that were loaded in some other request/ORM session), you must use a version number or timestamp.

To use optimistic locking for objects that do not have version or timestamp, you need to set attribute 'optimistic-lock' on the CFC. This attribute can take the following values:

  • all: This means that all the properties are included in the where clause of update query.

  • dirty (default): This means that only the modified properties are included in the where clause of the update query.

  • version: This means that only the version field is included in the where clause of update query.

  • none: This means that none of the properties are included in the where clause, which in effect means that optimistic concurrency is disabled for that component.

    Example:

    /** 
    * @persistent 
    * @table Users 
    * @optimistic-lock all 
    */ 
    component{ 
        property name="id" fieldtype="id" datatype="int" generator="native"; 
        property string fname; 
        property string lname; 
    }

Apart from defining optimistic lock control at the CFC level, you can also define it at the property level using 'optimisticlock' (true|false: default true) attribute.

You can specify optimisticlock=true for a property to acquire optimistic lock when the property is updated. Setting this attribute determines whether a version increment will occur when the property is dirty.

In case of one-to-many and many-to-many association, if the state of the collection changes, then version of the corresponding entity is incremented. It is advised that you disable this setting for one-to-many associations.