hibernate/hibernate-orm · error · OptimisticEntityLockException

Newer version [" + latestVersion + "] of entity [" + infoStr

Error message

Newer version [" + latestVersion + "] of entity [" + infoString( entry.getEntityName(), entry.getId() ) + "] found in database

What it means

EntityVerifyVersionProcess (EntityVerifyVersionProcess.java:40) runs at before-transaction-completion for entities with an optimistic @Version that were updated during the transaction. It re-reads the current version from the database and throws OptimisticEntityLockException ("Newer version [...] of entity [...] found in database") when the stored version no longer equals the one your flush used. This closes the gap between flush and commit: another transaction modified the row after your UPDATE was flushed but before your commit, i.e. a classic lost-update detected late.

Source

Thrown at hibernate-core/src/main/java/org/hibernate/action/internal/EntityVerifyVersionProcess.java:40

	private final Object object;

	/**
	 * Constructs an EntityVerifyVersionProcess
	 *
	 * @param object The entity instance
	 */
	public EntityVerifyVersionProcess(@Nonnull Object object) {
		this.object = object;
	}

	@Override
	public void doBeforeTransactionCompletion(@Nonnull SharedSessionContractImplementor session) {
		final var entry = session.getPersistenceContext().getEntry( object );
		// Don't check the version for an entity that is not in the PersistenceContext
		if ( entry != null ) {
			final Object latestVersion = entry.getPersister().getCurrentVersion( entry.getId(), session );
			if ( !entry.getVersion().equals( latestVersion ) ) {
				throw new OptimisticEntityLockException(
						object,
						"Newer version ["
								+ latestVersion
								+ "] of entity ["
								+ infoString( entry.getEntityName(), entry.getId() )
								+ "] found in database"
				);
			}
		}
	}
}

View on GitHub (pinned to fad1729dce)

Solutions

  1. Retry the whole business operation in a fresh transaction: catch OptimisticEntityLockException, reload the entity, reapply changes, commit - a standard optimistic retry loop
  2. Shrink the transaction: move the read-modify-write as late and as short as possible so the flush-to-commit gap is minimal
  3. For hot rows where retry storms are likely, switch that access to a pessimistic lock (LockModeType.PESSIMISTIC_WRITE / buildLockRequest) held for the transaction
  4. Make sure every writer goes through Hibernate so the version column is bumped consistently (no native UPDATEs bypassing the version)

Example fix

// before - single attempt, fails at commit with OptimisticEntityLockException
em.getTransaction().begin();
Account a = em.find(Account.class, id);
a.withdraw(amount);
em.getTransaction().commit();

// after - optimistic retry loop with fresh transaction per attempt
for (int i = 0; i < 5; i++) {
    try {
        em.getTransaction().begin();
        Account a = em.find(Account.class, id);
        a.withdraw(amount);
        em.getTransaction().commit();
        break;
    } catch (OptimisticEntityLockException e) {
        if (em.getTransaction().isActive()) em.getTransaction().rollback();
    }
}
Defensive patterns

Strategy: retry

Try / catch

// optimistic retry loop - each attempt gets a fresh transaction
for (int attempt = 0; attempt < MAX_RETRIES; attempt++) {
    try {
        tx = em.getTransaction(); tx.begin();
        Account a = em.find(Account.class, id);
        a.withdraw(amount);
        tx.commit();
        return;
    } catch (OptimisticEntityLockException | StaleObjectStateException e) {
        if (tx.isActive()) tx.rollback();
        em.clear(); // start the next attempt with fresh state
    }
}

Prevention

When it happens

Trigger: Versioned entity updated and flushed, then a concurrent transaction commits an update to the same row before your transaction commits; long-running transactions (extended @Transactional methods, OSIV request handling, batch jobs) where the post-flush window is large; two threads both bumping the same hot row.

Common situations: Conversation-style flows (page loads entity, user thinks, page saves) without stale-object state checks; background schedulers updating the same counters/rows as web requests; optimistic @Version columns that some code path bypasses (native SQL updates) so the version changed outside ORM control.

Related errors


AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22). Data as JSON: /api/errors/156ac9eff7a41407. Report an issue: GitHub.