Optimistic vs Pessimistic Locking in JPA
Fast recall
Quick revision
Core answer
@Version column — Hibernate issues UPDATE ... WHERE id = ? AND version = ? and throws OptimisticLockException if zero rows are updated. No DB lock is held; reads are non-blocking; the application must catch and retry. Pessimistic locking acquires a real database lock via SELECT ... FOR UPDATE (PESSIMISTIC_WRITE) or SELECT ... FOR SHARE (PESSIMISTIC_READ), held for the transaction's entire duration — other transactions block until commit, eliminating conflicts but serializing access and risking deadlocks. Choose optimistic for low-contention, read-heavy workloads (profile edits, catalog updates) where conflicts are rare and @Retryable handles them. Choose pessimistic for high-contention, short critical sections (seat reservations, account transfers, inventory decrements). The hidden cost of pessimistic locking is connection pool saturation — every locked row holds a connection until commit.Concurrency awareness — the candidate should understand both strategies, their DB-level implications, and how to implement each in JPA.
Treating optimistic locking as pessimistic, or not knowing that `OptimisticLockException` must be caught and the operation retried at the application layer.
Discuss `@Version` type choices (`int`, `long`, `Instant`), `@Retryable` for automatic retry with jitter, lock-ordering to prevent deadlocks, and how pessimistic locking affects connection pool sizing.
Interview-ready guide
Interview answer
Optimistic locking
- Add
@Version private Long version;to the entity - Hibernate appends
WHERE id = ? AND version = ?to every UPDATE; the version increments - If another transaction committed first, the version no longer matches;
OptimisticLockExceptionis thrown - No DB lock; reads non-blocking; conflicts surface only at commit
- Best for: user edits, e-commerce inventory with low contention
Pessimistic locking
@Lock(LockModeType.PESSIMISTIC_WRITE)on the repository method ->SELECT ... FOR UPDATE- Other transactions block until commit
- Best for: seat reservations, account transfers, hot-row contention
Handling `OptimisticLockException`
- Re-read and retry; never modify a stale entity
@Retryable(value = OptimisticLockException.class, maxAttempts = 3, backoff = @Backoff(delay = 100, random = true))- Cap attempts and add jitter to avoid retry storms
Avoiding deadlocks under pessimistic locking
- Always lock in a consistent order (e.g.
ORDER BY id ASC) - Set a
lock.timeoutso a stuck lock fails fast - Keep critical sections short — don't block on external I/O while holding a lock
Hybrid pattern
- Optimistic with
@Retryablefirst; if still failing, fall back to pessimistic on the hot row only
Optimistic vs Pessimistic Locking in JPA
Direct Answer
Optimistic locking detects conflicts at commit time using a @Version column — Hibernate issues UPDATE ... WHERE id = ? AND version = ? and throws OptimisticLockException if zero rows are updated. No DB lock is held; reads are non-blocking; the application must catch and retry. Pessimistic locking acquires a real database lock via SELECT ... FOR UPDATE (PESSIMISTIC_WRITE) or SELECT ... FOR SHARE (PESSIMISTIC_READ), held for the transaction's entire duration — other transactions block until commit, eliminating conflicts but serializing access and risking deadlocks. Choose optimistic for low-contention, read-heavy workloads (profile edits, catalog updates) where conflicts are rare and @Retryable handles them. Choose pessimistic for high-contention, short critical sections (seat reservations, account transfers, inventory decrements). The hidden cost of pessimistic locking is connection pool saturation — every locked row holds a connection until commit.
Common Mistakes & Pitfalls
Treating optimistic locking as pessimistic, or not knowing that OptimisticLockException must be caught and the operation retried at the application layer.
Check your understanding
Answer these without looking back at the article.
- How would you implement automatic retry for `OptimisticLockException` using Spring Retry?
- What happens if two transactions attempt to acquire `PESSIMISTIC_WRITE` on the same row simultaneously?
- Can you use optimistic locking on entities you did not create (e.g., third-party schemas without a version column)?
- How do you size the connection pool when pessimistic locking is the dominant pattern?
Related interview prep categories
2Looking for a structured roadmap?
Every module on this page is also part of the full Java backend interview track.