Transactions & Locking
Use transactions and row-level locks when writes must be consistent across multiple queries.
Transactional wrapper
Section titled “Transactional wrapper”$entityManager->transactional(function (EntityManager $em) use ($entity) { $em->persist($entity); $em->flush();
return $entity;});The wrapper commits when the callback returns and rolls back when the callback throws.
Manual control
Section titled “Manual control”beginTransaction()starts a transaction.commit()flushes and commits.rollback()rolls back pending database work.
Manual control is useful when a command needs to demonstrate intermediate failure states or lock behavior.
Locking
Section titled “Locking”Use lock() on the query builder for SELECT ... FOR UPDATE:
$stock = $entityManager ->createQueryBuilder(StockLock::class) ->where('product_id', $productId) ->lock() ->getSingleResult();An inventory-decrement flow locks stock rows before decrementing — lock() issues SELECT ... FOR UPDATE, so the row is held until the surrounding transaction commits or rolls back, and any other transaction trying to read or lock the same row blocks until then. That is what prevents two concurrent decrements from both reading the same starting quantity and overselling stock:
$entityManager->transactional(function (EntityManager $em) use ($productId, $qty) { $stock = $em ->createQueryBuilder(StockLock::class) ->where('product_id', $productId) ->lock() ->getSingleResult();
if ($stock->quantity < $qty) { throw new InsufficientStockException($productId); }
$stock->quantity -= $qty; $em->persist($stock); $em->flush();});Common pitfalls
Section titled “Common pitfalls”- Calling
lock()outside an active transaction raises a transaction-required error. - Acquire multiple locks in a deterministic order to reduce deadlock risk.
- Keep transaction callbacks small; long-running work should happen before or after the transaction where possible.
See also
Section titled “See also”Pessimistic locking (SELECT ... FOR UPDATE) blocks other writers for the duration of a transaction. Contrast with Optimistic Locking, which never locks and instead detects conflicts at write time.