Skip to content
AITroveRead. Build. Understand.
Make this comfortable

Spring Data JPA pessimistic lock: hold it only through the stock decision

Last updated: 1 Oct 20264 min read
tutorial
IntermediateBy AITrove Editorial

A PESSIMISTIC_WRITE query is useful only when the read and mutation share the transaction that holds the database lock.

Make the lock cover the decision

A stock allocator reads one DepotSlot, checks that 47 units remain, then subtracts 12. Without a concurrency control rule, two requests may both see 47. A repository query with PESSIMISTIC_WRITE can serialize that decision for the row, provided the service method is transactional and the database honors the lock mode. Returning the entity and deciding later, after commit, loses the protection.

Keep external work out

Do not call a payment service or broker while holding the row lock. Decide, persist, and commit; then use a transactional outbox for delivery. A lock wait can consume a pooled connection and request thread. Compare this path with optimistic locking when contention is rare.

Test the real database

Launch two transactions against the same slot and prove the accepted quantity never exceeds the starting balance. Include a timeout path and an independent tenant ID. The behavior of lock hints, isolation and timeout differs by database; an H2-only check is insufficient for a deployment claim. The code is an implementation sketch.

Implementation sketch

Java
@Lock(LockModeType.PESSIMISTIC_WRITE)
@Query("select s from DepotSlot s where s.tenantId = :tenantId " +
       "and s.sku = :sku")
Optional<DepotSlot> lockSlot(String tenantId, String sku);

@Transactional
public void allocate(String tenantId, String sku, int units) {
    DepotSlot slot = slots.lockSlot(tenantId, sku).orElseThrow();
    slot.reserve(units);
}

Cost and verification

A row lock trades conflict retries for waiting and possible deadlocks. It can lower throughput on a hot SKU, so bound the transaction and record lock-wait latency.

Common Mistakes

  • Do not assume the lock survives after the repository transaction returns.
  • Do not hold the lock across remote calls.
  • Do not treat an in-memory database concurrency test as proof for the deployed database.

Read next

Spring JPA optimistic locking: reject a stale stock update, Spring transaction isolation: state the anomaly you need to prevent, Spring outbox transaction boundaries: where atomicity ends, Spring Data JPA bulk update: the managed entity can still hold the old value.

spring
spring-boot
data-transactions
jpa-pessimistic-lock-transaction
Storage details