`@Entity public class Course {
2.5.8. Mapping optimistic locking
<div class="paragraph">
JPA defines support for optimistic locking based on either a version (sequential numeric) or timestamp strategy.
To enable this style of optimistic locking simply add the `javax.persistence.Version` to the persistent attribute that defines the optimistic locking value.
According to JPA, the valid types for these attributes are limited to:
</div>
<div class="ulist">
int
orInteger
short
orShort
long
orLong
java.sql.Timestamp
</div>Example 96. `@Version` annotation mapping@Id private Integer id; @Version private Integer version; ...
}
</pre> </div> </div> <div class="listingblock"> <div class="content"> <pre class="prettyprint highlight">
@Entity public class Thing {@Id private Integer id; @Version private Timestamp ts; ...
}
</pre> </div> </div> <div class="listingblock"> <div class="content"> <pre class="prettyprint highlight">
@Entity public class Thing2 {@Id private Integer id; @Version private Instant ts; ...
}`</pre> </div> </div> </div> </div>
Versionless optimistic locking
Although the default
@Version
property optimistic locking mechanism is sufficient in many situations, sometimes, you need rely on the actual database row column values to prevent lost updates.</div>
Hibernate supports a form of optimistic locking that does not require a dedicated "version attribute". This is also useful for use with modeling legacy schemas.
</div>
The idea is that you can get Hibernate to perform "version checks" using either all of the entity’s attributes, or just the attributes that have changed. This is achieved through the use of the
@OptimisticLocking
annotation which defines a single attribute of typeorg.hibernate.annotations.OptimisticLockType
.</div>
There are 4 available OptimisticLockTypes:
</div>
- `NONE`
optimistic locking is disabled even if there is a
@Version
annotation present</dd>
- `VERSION` (the default)
performs optimistic locking based on a
@Version
as described above</dd>
- `ALL`
performs optimistic locking based on all fields as part of an expanded WHERE clause restriction for the UPDATE/DELETE SQL statements
</dd>
- `DIRTY`
performs optimistic locking based on dirty fields as part of an expanded WHERE clause restriction for the UPDATE/DELETE SQL statements.
</dd> </dl> </div>
Versionless optimistic locking using
OptimisticLockType.ALL
Example 97. `OptimisticLockType.ALL` mapping example`@Entity(name = "Person") @OptimisticLocking(type = OptimisticLockType.ALL) @DynamicUpdate public static class Person {
@Id private Long id; @Column(name = "`name`") private String name; private String country; private String city; @Column(name = "created_on") private Timestamp createdOn; //Getters and setters are omitted for brevity
}`</pre> </div> </div> </div> </div>
When you need to modify the
Person
entity above:</div>
Example 98. `OptimisticLockType.ALL` update example`Person person = entityManager.find( Person.class, 1L ); person.setCity( "Washington D.C." );`
`UPDATE
Person
SET
city=?
WHERE
id=? AND city=? AND country=? AND created_on=? AND "name"=?
-- binding parameter [1] as [VARCHAR] - [Washington D.C.] -- binding parameter [2] as [BIGINT] - [1] -- binding parameter [3] as [VARCHAR] - [New York] -- binding parameter [4] as [VARCHAR] - [US] -- binding parameter [5] as [TIMESTAMP] - [2016-11-16 16:05:12.876] -- binding parameter [6] as [VARCHAR] - [John Doe]`</pre> </div> </div> </div> </div>
As you can see, all the columns of the associated database row are used in the
WHERE
clause. If any column has changed after the row was loaded, there won’t be any match, and aStaleStateException
or anOptimisticLockException
is going to be thrown.</div>
</td>
When using
OptimisticLockType.ALL
, you should also use@DynamicUpdate
because theUPDATE
statement must take into consideration all the entity property values.</div> </td> </tr> </table> </div> </div>
Versionless optimistic locking using
OptimisticLockType.DIRTY
The
OptimisticLockType.DIRTY
differs fromOptimisticLockType.ALL
in that it only takes into consideration the entity properties that have changed since the entity was loaded in the currently running Persistence Context.</div>
Example 99. `OptimisticLockType.DIRTY` mapping example`@Entity(name = "Person") @OptimisticLocking(type = OptimisticLockType.DIRTY) @DynamicUpdate @SelectBeforeUpdate public static class Person {
@Id private Long id; @Column(name = "`name`") private String name; private String country; private String city; @Column(name = "created_on") private Timestamp createdOn; //Getters and setters are omitted for brevity
}`</pre> </div> </div> </div> </div>
When you need to modify the
Person
entity above:</div>
Example 100. `OptimisticLockType.DIRTY` update example`Person person = entityManager.find( Person.class, 1L ); person.setCity( "Washington D.C." );`
`UPDATE
Person
SET
city=?
WHERE
id=? and city=?
-- binding parameter [1] as [VARCHAR] - [Washington D.C.] -- binding parameter [2] as [BIGINT] - [1] -- binding parameter [3] as [VARCHAR] - [New York]`</pre> </div> </div> </div> </div>
This time, only the database column that has changed was used in the
WHERE
clause.</div>
</td>
The main advantage of
OptimisticLockType.DIRTY
overOptimisticLockType.ALL
and the defaultOptimisticLockType.VERSION
used implicitly along with the@Version
mapping, is that it allows you to minimize the risk ofOptimisticLockException
across non-overlapping entity property changes.</div>
When using
OptimisticLockType.DIRTY
, you should also use@DynamicUpdate
because theUPDATE
statement must take into consideration all the dirty entity property values, and also the@SelectBeforeUpdate
annotation so that detached entities are properly handled by theSession#update(entity)
operation.</div> </td> </tr> </table> </div> </div> </div> </div>