Hibernate Locking

Introduction

Locking refers to a mechanism taken for granted in a relational database to prevent any modification to the data between the time the data are read and the time the data are used.

There are mainly two types of locking strategy available – optimistic and pessimistic

Optimistic

  • Multiple transactions can complete without affecting each other
  • Transactions can proceed without locking the data resources that they affect
  • Before committing, each transaction verifies that no other transaction has modified its data
  • If conflicting modification is revealed, then the committing transaction rolls back

When your application uses long transactions or conversations that span several database transactions, you can store versioning data, so that if the same entity is updated by two transactions, to commit the modification changed by last transaction is informed of the conflict, and does not override the other transaction’s work. This approach guarantees some isolation, but scales well and works particularly well in read-frequently write-sometimes situations.

Hibernate provides two different mechanisms for storing versioning information, a dedicated version number and a timestamp.

Version Number

The version number mechanism for optimistic locking is provided through a @Version annotation.

@Entity
public class Person {
...
    @Version
    @Column(name="VEROPTLOCK")
    public Integer getVersion() { ... }
}

Here, the version property is mapped to the VEROPTLOCK column, and the entity manager uses it to detect conflicting updates, and prevent the loss of updates that would be overwritten by a last-commit-wins strategy.

If you want the version number to be generated by the database, such as a trigger, use the annotation:

@org.hibernate.annotations.Generated(GenerationTime.ALWAYS)

Declaring a version property in *.hbm.xml

<version
        column="version_column"
        name="propertyName"
        type="typename"
        access="field|property|ClassName"
        unsaved-value="null|negative|undefined"
        generated="never|always"
        insert="true|false"
        node="element-name|@attribute-name|element/@attribute|."
/>
  • column – the name of the column holding the version number. Optional, defaults to the property name.
  • name – the name of a property of the persistent class.
  • type – the type of the version number. Optional, defaults to integer.
  • access – Hibernate’s strategy for accessing the property value. Optional, defaults to property.
  • unsaved-value – indicates that an instance is newly instantiated and thus unsaved. This distinguishes it from detached instances that were saved or loaded in a previous session. The default value, undefined, indicates that the identifier property value should be used. Optional.
  • generated – indicates that the version property value is generated by the database. Optional, defaults to never.
  • insert – whether or not to include the version column in SQL insert statements. Defaults to true, but you can set it to false if the database column is defined with a default value of 0.
Timestamp

Timestamps are a less reliable way of optimistic locking than version numbers, but can be used by applications for other purposes as well. Timestamping is automatically used if you the @Version annotation on a Date or Calendar.

@Entity
public class Person {
...
    @Version
    public Date getLastUpdate() { ... }
}

Hibernate can retrieve the timestamp value from the database or the JVM, by reading the value you specify for the @org.hibernate.annotations.Source annotation. The value can be either org.hibernate.annotations.SourceType.DB or org.hibernate.annotations.SourceType.VM. The default behavior is to use the database, and is also used if you don’t specify the annotation at all.

The timestamp can also be generated by the database instead of Hibernate, if you use the @org.hibernate.annotations.Generated(GenerationTime.ALWAYS) annotation.

The timestamp element in *.hbm.xml

<timestamp
        column="timestamp_column"
        name="propertyName"
        access="field|property|ClassName"
        unsaved-value="null|undefined"
        source="vm|db"
        generated="never|always"
        node="element-name|@attribute-name|element/@attribute|."
/>
  • column – the name of the column holding the timestamp. Optional, defaults to the property name.
  • name – the name of a JavaBeans style property of Java type Date or Timestamp of the persistent class.
  • type – the type of the version number. Optional, defaults to integer.
  • access – Hibernate’s strategy for accessing the property value. Optional, defaults to property.
  • unsaved-value – indicates that an instance is newly instantiated and thus unsaved. This distinguishes it from detached instances that were saved or loaded in a previous session. The default value, undefined, indicates that the identifier property value should be used. Optional.
  • generated – indicates that the timestamp property value is generated by the database. Optional, defaults to never.
  • source – Database-based timestamps incur an overhead because Hibernate needs to query the database each time to determine the incremental next value. However, database-derived timestamps are safer to use in a clustered environment. Not all database dialects are known to support the retrieval of the database’s current timestamp. Others may also be unsafe for locking, because of lack of precision.

Pessimistic

  • Concurrent transactions conflict with each other
  • Concurrent transactions require resources to be locked

Typically, you only need to specify an isolation level for the JDBC connections and let the database handle locking issues.

The available lock modes from LockMode class are as follows:

LockMode.NONE: No lock occurs with the row in the database. Thus any thread can make a change to the object. This indicates an absence of a lock. All objects switch to this lock mode at the end of a Transaction. Objects associated with the session via a call to update() or saveOrUpdate() also start out in this lock mode.

LockMode.READ: A read lock is acquired automatically and occurs on the object.

LockMode.UPGRADE: A lock is acquired upon user’s request and occurs on the object row, and the in-memory object is updated with the object information in the database table.

LockMode.UPGRADE_NOWAIT: An upgrade lock is acquired upon user’s request and attempted using a SELECT FOR UPDATE NOWAIT.

LockMode.WRITE: A write lock is acquired automatically and obtained when the object is either inserted (for a new object) or updated.

Some locks are more expensive than others, and you should take care when obtaining a lock. Depending on your choice, some threads using the same object may be blocked from updating the database.

The explicit user request mentioned above occurs as a consequence of any of the following actions:

  • A call to Session.load(), specifying a LockMode.
  • A call to Session.lock().
  • A call to Query.setLockMode().

If you call Session.load() with option UPGRADE or UPGRADE_NOWAIT, and the requested object is not already loaded by the session, the object is loaded using SELECT ... FOR UPDATE. If you call load() for an object that is already loaded with a less restrictive lock than the one you request, Hibernate calls lock() for that object.

Session.lock() performs a version number check if the specified lock mode is READ, UPGRADE, or UPGRADE_NOWAIT. In the case of UPGRADE or UPGRADE_NOWAIT, SELECT ... FOR UPDATE syntax is used.

If the requested lock mode is not supported by the database, Hibernate uses an appropriate alternate mode instead of throwing an exception. This ensures that applications are portable.

You can find more information on https://docs.jboss.org/hibernate/orm/4.0/devguide/en-US/html/ch05.html

Thanks for reading.

Leave a Reply

Your email address will not be published. Required fields are marked *