Hibernate Versionless Optimistic Locking
This article is about a Hibernate feature called versionless optimistic locking. Versionless optimistic locking is an alternative to using JPA’s @Version
annotation with a numeric or timestamp column. It can be used to lower the risk of optimistic locking exceptions.
The @OptimisticLocking
Annotation
Optimistic locking with Hibernate is commonly implemented by applying the @Version
annotation to a version
instance variable resulting in a VERSION
column with either a numberic value or a timestamp (depending on the declared type of the instance variable).
Although this is sufficient in many situations, Hibernate does provide alternative locking strategies too. By applying the @OptimisticLocking
annotation on type level you can define the style of optimistic locking to be applied to a JPA entity.
import org.hibernate.annotations.OptimisticLocking;
import org.hibernate.annotations.OptimisticLockType;
@Entity
@OptimisticLocking(type = OptimisticLockType.VERSION)
@Getter
@Setter
public class Car {
@Id
private Integer id;
@Version
private Integer version;
}
The example above shows the default locking strategy. When having a look at the OptimisticLockType
(JavaDoc) enumeration, we can see the following locking strategies:
- ALL
- DIRTY
- NONE
- VERSION
ALL
and DIRTY
define so-called versionless locking strategies.
If OptimisticLockType.ALL
is given in the @OptimisticLocking#type
attribute, the optimistic lockingn strategy works as follows: whenever an UPDATE
or DELETE
statement is generated by Hibernate, it will append not only the @Id
column in its WHERE
clause but also all of the persistent fields.
Let’s say our Car
class would look like
import org.hibernate.annotations.OptimisticLocking;
import org.hibernate.annotations.OptimisticLockType;
@Entity
@OptimisticLocking(type = OptimisticLockType.ALL)
@Getter
@Setter
public class Car {
@Id
private Integer id;
private String model;
private String brand;
}
let us consider we have an already persisted Car
instance and we would change property model
Car car = carRepository.findOne(carId);
car.setModel("Punto");
car = carRepository.save(car);
with OptimisticLockType.ALL
the generated SQL statement would be
UPDATE CAR SET MODEL = ?, BRAND = ? WHERE ID = ? AND MODEL = ? AND BRAND = ?
All columns of the associated database row are used in the WHERE clause. If the Car
instance would have been changed in the meantime and the UPDATE
would not find a row, a StaleStateException
or an OptimisticLockException
is going be thrown.
With OptimisticLockType.DIRTY
only the dirty/changed fields (in our case model
) would be part of the SQL statement
UPDATE CAR SET MODEL = ?, BRAND = ? WHERE ID = ? AND MODEL = ?
If the model
column still has the same value the UPDATE
will succeed.
With such an optimistic locking strategy, the main advantage is that it reduces the potential of overlapping updates. When using OptimisticLockType.DIRTY
, the documentation advises to use the @DynamicUpdate
and @SelectBeforeUpdate
annotations in combination with @OptimisticLocking
. @DynamicUpdate
will generate UPDATE
statements only SET
ting the dirty columns, @SelectBeforeUpdate
will avoid issues with detached entities, assuming the Hibernate session is opened for the entire request.
Summary
Besides the commonly known versioning optimistic locking strategy, Hibernate comes with two other versionless strategies this article has a closer look at.