OptimisticLockException signals that an optimistic concurrency check failed. With an @Version field, Hibernate includes the entity’s loaded version in an UPDATE or DELETE condition. If the statement affects no rows, Hibernate detects the conflict during flushing: the row may have changed or been deleted. section 11.2.2
First commit wins
Two transactions load an Item at version 0. The first commits an update at version 1. The second transaction’s update cannot match version 0, so flushing throws OptimisticLockException. The user can be asked to restart with fresh data. 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 listing shows the versioned UPDATE and the flush call expected to throw the exception. section 11.2.2
