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 or Integer
  • short or Short
  • long or Long
  • java.sql.Timestamp </div>

    Example 96. `@Version` annotation mapping
    `@Entity
    public class Course {

    @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 type org.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." );`
    </div>
    `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 a StaleStateException or an OptimisticLockException is going to be thrown.

    </div>

    </td>

    When using OptimisticLockType.ALL, you should also use @DynamicUpdate because the UPDATE 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 from OptimisticLockType.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." );`
    </div>
    `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 over OptimisticLockType.ALL and the default OptimisticLockType.VERSION used implicitly along with the @Version mapping, is that it allows you to minimize the risk of OptimisticLockException across non-overlapping entity property changes.

    </div>

    When using OptimisticLockType.DIRTY, you should also use @DynamicUpdate because the UPDATE statement must take into consideration all the dirty entity property values, and also the @SelectBeforeUpdate annotation so that detached entities are properly handled by the Session#update(entity) operation.

    </div> </td> </tr> </table> </div> </div> </div> </div>

results matching ""

    No results matching ""