Doctrine transaction processing for multiple object managers

Both transactions must be rejected if:

  • $em1

    fails but $em2

    succeeds.
  • $em1

    succeeds but $em2

    fails.

So my example below is the correct way to deal with transactions when more than one EM is involved? I came up with this after reading transactions and Concurrency documentation.

$em1->getConnection()->beginTransaction();
$em2->getConnection()->beginTransaction();

try {
     $em1->persist($object1);
     $em1->flush();
     $em1->getConnection()->commit();

     $em2->persist($object2);
     $em2->flush();
     $em2->getConnection()->commit();
} catch (Exception $e) {
     $em1->getConnection()->rollback();
     $em2->getConnection()->rollback();
}

      

The reason I am trying to implement this is because I am getting an error ....resulted in a Doctrine\ORM\ORMException exception (The EntityManager is closed.)

somewhere along the line in the application. Maybe I can handle this method below, but I think it's better to use transaction for the business logic above.

private function getNewEntityManager($em)
{
    if (!$em->isOpen()) {
        $em = $em->create($em->getConnection(), $em->getConfiguration());
    }

    return $em;
}

      

+3


source to share


2 answers


Your example code does work, which surprises me because Francesco Panina is (or should be) correct that $em1->getConnection()->commit()

will commit the first transaction, and you will lose [sic] the privilege to rollback such a transaction if an error occurs in the second transaction.

However, something about Doctrine's handling of transaction nesting levels means that you can actually rollback the first transaction when an error occurs from the second transaction.



However, the best practice would be to not depend on this behavior and instead put both commits at the very end of the try block like so:

$em1->getConnection()->beginTransaction();
$em2->getConnection()->beginTransaction();

try {
    $em1->persist($object1);
    $em1->flush();

    $em2->persist($object2);
    $em2->flush();

    $em1->getConnection()->commit();
    $em2->getConnection()->commit();
} catch (Exception $e) {
    $em1->getConnection()->rollback();
    $em2->getConnection()->rollback();
    throw $e;
}

      

With this slight change, your example demonstrates the correct way to handle transactions spanning multiple entity managers.

+1


source


I would like to point out a few things that may clarify your question:

I am not aware of the process you are using to create the second object manager, please be aware that 2 completely different object managers will not use the same connection. Can you specify your use case for two different entity managers?

Consider the operation:

$em1->getConnection()->commit();

      

will commit the first transaction, and you will lose the rollback privilege of such a transaction if an error occurs from the second transaction.




 Doctrine\ORM\ORMException exception (The EntityManager is closed.)

      

This is typical when you are trying to use any commit / reset operation after a DBAL (database related) exception has been thrown; in this case, Doctrine's default behavior is to close the object manager. And it's common practice to do this after any rollback:

$em1->getConnection()->rollback();
$em1->close();

      

Hope this helps, Best wishes.

0


source







All Articles