liveBook · Manning

Optimistic concurrency control

As explained in Java Persistence with Spring Data and Hibernate epub

Optimistic concurrency control detects conflicting updates by checking that data has not changed since it was read, typically through version information.

Optimistic concurrency control fits multiuser applications where concurrent changes are uncommon and a conflict can be detected late in a unit of work. JPA can detect conflicts by checking an entity’s version rather than relying solely on the database’s transaction isolation. section 11.2.2 section 11.2

Listing 11.1 Enabling versioning on a mapped entity
Path: Ch11/transactions/src/main/java/com/manning/javapersistence/ch11
 /concurrency/Item.java
 
@Entity
public class Item {
    @Version
    private long version;
    //  . . .
}

Add an @Version property to a JPA entity to enable automatic version checking. Its value is for concurrency control; application code should not change it. section 11.2.2

When Hibernate flushes a changed entity, it increments the version and checks the previously loaded value in the UPDATE condition. For example, `UPDATE ITEM SET NAME = ?, VERSION = 1 WHERE ID = ? AND VERSION = 0` succeeds only if that version still matches. Hibernate also checks versions for DELETE statements. This provides a first-commit-wins approach for concurrently modified entities. section 11.2.2

Listing 11.2 Hibernate incrementing and checking the version automatically
Path: /Ch11/transactions/src/test/java/com/manning/javapersistence/ch11
 /concurrency/Versioning.java – firstCommitWins()
 
   EntityManager em1 = emf.createEntityManager();
   em1.getTransaction().begin();
Item item = em1.find(Item.class, ITEM_ID);                             #1
// select * from ITEM where ID = ?
assertEquals(0, item.getVersion());                                    #2
item.setName("New Name");
// . . .  Another transaction changes the record
assertThrows(OptimisticLockException.class, () -> em1.flush());        #3
// update ITEM set NAME = ?, VERSION = 1 where ID = ? and VERSION = 0
#1 Retrieving an entity instance by identifier loads the current version from the database with a <kbd>SELECT</kbd>.
#2 The current version of the <kbd>Item</kbd> instance is <kbd>0</kbd>.
#3 When the persistence context is flushed, Hibernate detects the dirty <kbd>Item</kbd> instance and increments its version to <kbd>1</kbd>. SQL <kbd>UPDATE</kbd> now performs the version check, storing the new version in the database, but only if the database version is still <kbd>0</kbd>.

The competing-update example loads version 0, changes the entity, then receives an OptimisticLockException on flush after another transaction changes the record. section 11.2.2

Choose the check that matches the work being protected:

  • Use a numeric @Version property for automatic checking; Hibernate manages the counter. section 11.2.2
  • Use a LocalDateTime @Version property when mapping a timestamp version. section 11.2.2
  • Request LockModeType.OPTIMISTIC on a query when a calculation based on read entities needs a version check even without changing them. section 11.2.2
  • Request LockModeType.OPTIMISTIC_FORCE_INCREMENT when an operation must increment an entity’s version, as in work involving an item and its bids. section 11.2.2
  • Consider Hibernate’s OptimisticLocking with type ALL and DynamicUpdate for a different mapping: its illustrated UPDATE checks the previously read column values in the WHERE clause. section 11.2.2

Version checking complements database concurrency control; it does not replace transaction boundaries. Handle a detected conflict as a transaction failure rather than assuming the attempted update was saved. section 11.2 section 11.2.2 section 11.5

How Java Persistence with Spring Data and Hibernate epub covers it

How deep it goes
Details version mapping, flush-time SQL checks, manual lock modes, timestamps, and a Hibernate-specific alternative; treats database isolation separately.
Examples
Uses a conflicting item update, a category-price query with OPTIMISTIC, and a bid operation with OPTIMISTIC_FORCE_INCREMENT.
What it assumes
Assumes familiarity with Java entities, EntityManager transactions, and SQL; introduces version mapping and lock modes through code.
What you can do after
Apply it via JPA @Version and LockModeType to detect conflicting updates or request checks for work based on reads.
How it connects
Relates version checking to transaction isolation, persistence-context flushing, pessimistic locking, and transaction boundaries.

Also explained in

Compare how 3 books explain optimistic concurrency control →

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Java Persistence with Spring Data and Hibernate epub. The section links point to the parts of the book it draws on.