java persistence_error ai_generated true

javax.persistence.OptimisticLockException: Row was updated or deleted by another transaction

ID: java/optimistic-lock-exception

Also available as: JSON · Markdown
85%Fix Rate
88%Confidence
70Evidence
2010-01-01First Seen

Version Compatibility

VersionStatusIntroducedDeprecatedNotes
17 active

Root Cause

Another transaction modified the same entity between read and write. The @Version field doesn't match, so JPA/Hibernate rejects the update to prevent lost updates. Common in high-concurrency scenarios.

generic

Workarounds

  1. 90% success Implement retry logic that re-fetches the entity before each attempt
    int retries = 3;
    while (retries-- > 0) {
        try {
            Entity e = repository.findById(id).orElseThrow();
            e.setField(newValue);
            repository.save(e);
            break;
        } catch (OptimisticLockException ex) {
            if (retries == 0) throw ex;
        }
    }

    Sources: https://docs.jboss.org/hibernate/orm/6.2/userguide/html_single/Hibernate_User_Guide.html#locking-optimistic

  2. 88% success Use pessimistic locking for high-contention entities
    @Lock(LockModeType.PESSIMISTIC_WRITE)
    @Query("SELECT e FROM Entity e WHERE e.id = :id")
    Optional<Entity> findByIdForUpdate(@Param("id") Long id);

    Sources: https://docs.spring.io/spring-data/jpa/reference/jpa/locking.html

Dead Ends

Common approaches that don't work:

  1. Removing the @Version field to disable optimistic locking 80% fail

    Removes the safety mechanism that prevents lost updates. Concurrent modifications will silently overwrite each other, causing data corruption.

  2. Catching the exception and blindly retrying the same update 90% fail

    Retrying with the same stale data will hit the same version conflict. The entity must be re-fetched before retrying.

Error Chain

Frequently confused with: