Home/Learn/Hibernate & JPA/Optimistic Locking — @Version

Optimistic Locking — @Version

Intermediate
Transactions

@Version on an integer/timestamp field enables optimistic locking; Hibernate includes the version in UPDATE WHERE clauses and throws OptimisticLockException on concurrent modification.

Overview

Optimistic locking is a concurrency control strategy based on the assumption that conflicts are rare. Instead of holding a database lock for the duration of a transaction (pessimistic locking), optimistic locking allows multiple transactions to read the same data simultaneously. At commit time, it checks whether anyone else modified the record since you read it — if so, the transaction fails with an OptimisticLockException. In JPA/Hibernate, optimistic locking is implemented with a single @Version field. Hibernate automatically includes the version in UPDATE/DELETE WHERE clauses: if the version in the DB no longer matches what you read, zero rows are updated and Hibernate throws OptimisticLockException. This is the preferred locking strategy for most web applications because it maximises concurrency without holding DB locks.

Implementing @Version

Add a @Version field (Integer or Long is most common; Instant/Timestamp also works) to your entity. Hibernate manages the value automatically — you never set it manually. On INSERT it starts at 0; on every UPDATE it increments. If an UPDATE affects 0 rows (version mismatch), Hibernate throws OptimisticLockException (JPA) or StaleObjectStateException (Hibernate).

Java — @Version Entity
@Entity
public class BankAccount {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String owner;
    private BigDecimal balance;

    @Version   // Hibernate manages this — do NOT set it manually
    private Integer version;   // starts at 0, incremented on every UPDATE
}

// What Hibernate generates for an UPDATE:
// UPDATE bank_account
//   SET balance = ?, version = 1        ← increment version
//  WHERE id = 42
//   AND version = 0;                    ← must match what we read!
//
// If another transaction already updated version to 1,
// this WHERE matches 0 rows → Hibernate throws OptimisticLockException

Handling OptimisticLockException

When two users edit the same record simultaneously, one will get OptimisticLockException. The correct response is NOT to swallow the exception — it means another user's changes would be overwritten (lost update). You should either retry the operation (re-read, re-apply changes) or surface a user-friendly error asking the user to refresh and try again.

In a Spring REST API, catch OptimisticLockException (or its wrapper ObjectOptimisticLockingFailureException from Spring Data) in your @RestControllerAdvice and return HTTP 409 Conflict.

Java — Handling the Exception
// Service — retry on conflict (for automated processes)
@Service
@RequiredArgsConstructor
public class AccountService {

    private final AccountRepository accountRepo;

    @Transactional
    public void debit(Long accountId, BigDecimal amount) {
        BankAccount account = accountRepo.findById(accountId).orElseThrow();
        if (account.getBalance().compareTo(amount) < 0) {
            throw new InsufficientFundsException();
        }
        account.setBalance(account.getBalance().subtract(amount));
        // save() triggers UPDATE with version check
    }
}

// @RestControllerAdvice — return 409 Conflict for concurrent modification
@ExceptionHandler({
    OptimisticLockException.class,
    ObjectOptimisticLockingFailureException.class
})
@ResponseStatus(HttpStatus.CONFLICT)
public ApiError handleConcurrencyConflict(Exception ex) {
    return ApiError.builder()
        .status(409)
        .error("Conflict")
        .message("This record was modified by another user. Please refresh and try again.")
        .timestamp(Instant.now())
        .build();
}

Optimistic vs Pessimistic Locking

Pessimistic locking acquires a DB-level lock (SELECT ... FOR UPDATE) when reading, holding it until the transaction commits. No other transaction can modify the row until the lock is released. Use pessimistic locking when conflicts are frequent or when you cannot afford to retry (e.g., ticket booking where two users race for the last seat).

Optimistic locking is better for most web applications: high concurrency, low conflict rate, users editing their own data. Pessimistic locking is better when conflict rate is high and retry cost is unacceptable.

Java — Pessimistic vs Optimistic
// Pessimistic locking in JPA — holds a DB lock until transaction ends
@Repository
public interface AccountRepository extends JpaRepository<BankAccount, Long> {

    @Lock(LockModeType.PESSIMISTIC_WRITE)   // SELECT ... FOR UPDATE
    @Query("SELECT a FROM BankAccount a WHERE a.id = :id")
    Optional<BankAccount> findByIdForUpdate(@Param("id") Long id);
}

// When to use each:
// ✅ Optimistic (@Version) — users editing their own profile, product descriptions
//    Low conflict, read-heavy, retries acceptable
//
// ✅ Pessimistic (PESSIMISTIC_WRITE) — inventory decrement, seat booking,
//    wallet debit — high conflict, cannot afford lost update or retry

Key Points to Remember

  • 1@Version adds a version column Hibernate includes in every UPDATE/DELETE WHERE clause — if the version changed, 0 rows are affected and OptimisticLockException is thrown.
  • 2Optimistic locking maximises concurrency by not holding DB locks; it detects conflicts only at commit time.
  • 3OptimisticLockException means a lost update was prevented — never swallow it; return HTTP 409 Conflict or retry the operation.
  • 4Pessimistic locking (SELECT FOR UPDATE) holds a DB lock for the transaction duration — use when conflicts are frequent or retry is not acceptable.
  • 5Never set the @Version field manually in your code — Hibernate manages it exclusively.
  • 6@Version on Instant/Timestamp can have sub-millisecond granularity issues; prefer Integer or Long version counters.

Interview Questions

Sign in to ask Aria
1

How does optimistic locking work in JPA/Hibernate with @Version?

MediumAmazon
2

What is OptimisticLockException and how should you handle it in a REST API?

MediumFlipkart
3

When would you use pessimistic locking instead of optimistic locking?

MediumUber
4

Two users simultaneously edit the same product. How does @Version prevent a lost update?

MediumThoughtworks
5

What SQL does Hibernate generate for an UPDATE when @Version is present?

HardNetflix

Ask Aria about Optimistic Locking — @Version

Your personal AI tutor — ask anything about this concept

Revision Status

Personal Notes

Sign in to save personal notes for this topic.

Discussion

Sign in to join the discussion.

Loading discussion…