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
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


