Skip to content

Instantly share code, notes, and snippets.

@rmrfself
Created May 19, 2012 07:27
Show Gist options
  • Star 33 You must be signed in to star a gist
  • Fork 6 You must be signed in to fork a gist
  • Save rmrfself/2729873 to your computer and use it in GitHub Desktop.
Save rmrfself/2729873 to your computer and use it in GitHub Desktop.
How to make transactions in Doctrine 2 using Symfony 2
<?php
// Snippet for Symfony 2 application that uses Doctrine 2 to handle transactions
// It uses the names of the objects/doctrine repositories from the Beta 4 Manual of Symfony 2.
// Get the entity manager
$em = $this->getDoctrine()->getEntityManager();
// suspend auto-commit
$em->getConnection()->beginTransaction();
// Try and make the transaction
try {
// Get the account
$account = $this->getDoctrine()->getRepository('AcmeStoreBundle:Account')->find($id);
// Lower balance
$account->setBalance($account->getBalance() - 100);
// Save account
$em->persist($account);
$em->flush();
// Try and commit the transaction
$em->getConnection()->commit();
} catch (Exception $e) {
// Rollback the failed transaction attempt
$em->getConnection()->rollback();
throw $e;
}
@abienvenu
Copy link

In high concurrency environment, two processes running at the same time could produce a balance decreased by 100 instead of expected 200. It would be very bad luck if both threads would have executed $account->getBalance() before any of them had executed $account->setBalance(), but it happens. Beginning a transaction ensures consistent reads, but in this example one should take a write lock on $account before reading and decreasing the balance.

@leongersen
Copy link

Note: If you are reusing the $em after the rollback (for example to log the exception), the $account entity will still have the renewed balance, and calling flush will flush the new value to the database.

To work around this, call $em->clear() to detach all entities after the rollback. See this stackOverflow question for details.

@Steveb-p
Copy link

To make absolutely sure that whatever your process is trying to do is done on most recent database data, you might want to include version field into Doctrine Entity definition.

See http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/transactions-and-concurrency.html#locking-support

@Oliboy50
Copy link

Oliboy50 commented Sep 8, 2017

@Steveb-p

From your link:

Database transactions are fine for concurrency control during a single request. However, a database transaction should not span across requests, the so-called “user think time”. Therefore a long-running “business transaction” that spans multiple requests needs to involve several database transactions. Thus, database transactions alone can no longer control concurrency during such a long-running business transaction. Concurrency control becomes the partial responsibility of the application itself.

So, IMHO, it doesn't have anything to do with "being absolutely sure that whatever your process is trying to do is done on most recent database data", it only depends on if you need transactions on multiple requests or not, right?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment