Symfony 在多个实体管理器上处理条令事务

Symfony 在多个实体管理器上处理条令事务,symfony,doctrine-orm,transactions,Symfony,Doctrine Orm,Transactions,如果出现以下情况,则应回滚两个事务: $em1失败,但$em2成功 $em1成功,但$em2失败 那么,我下面的例子是否正确处理涉及多个EMs的交易?我是在阅读了文档后想到的 $em1->getConnection()->beginTransaction(); $em2->getConnection()->beginTransaction(); try { $em1->persist($object1); $em1->flush();

如果出现以下情况,则应回滚两个事务:

  • $em1
    失败,但
    $em2
    成功
  • $em1
    成功,但
    $em2
    失败
  • 那么,我下面的例子是否正确处理涉及多个EMs的交易?我是在阅读了文档后想到的

    $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();
    }
    
    我之所以尝试实现这一点,是因为我得到了
    …导致了一个条令\ORM\orException异常(EntityManager已关闭)。
    应用程序中的某个错误。我可能可以用下面的方法处理它,但我认为在上面的业务逻辑中使用事务更好

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

    我想指出几件事,可能会让你头脑清醒:

    我不知道创建第二个实体管理器的过程,请记住,两个完全不同的实体管理器不会共享相同的连接。您能指出两个不同实体管理器的使用案例吗

    考虑到该行动:

    $em1->getConnection()->commit();
    
    将提交第一个事务,如果第二个事务出现错误,您将失去回滚该事务的权限


    在抛出DBAL(数据库相关)异常后尝试操作任何提交/刷新操作时,这是典型的情况;在这种情况下,默认行为是关闭实体管理器。 通常的做法是在任何回滚后执行此操作:

    $em1->getConnection()->rollback();
    $em1->close();
    
    希望有帮助,
    尊敬。

    您的示例代码确实有效,这让我感到惊讶,因为Francesco Panina在
    $em1->getConnection()->commit()方面是正确的(或者应该是正确的)

    将提交第一个事务,如果第二个事务出现错误,您将失去[sic]回滚该事务的权限

    然而,条令处理的方式意味着,当第二个事务出现错误时,实际上仍然可以回滚第一个事务

    尽管如此,最佳做法是不依赖于此行为,而是将两个提交放在try块的最末端,如下所示:

    $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;
    }
    

    通过这一小改动,您的示例确实演示了处理跨多个实体经理的事务的正确方法。

    谢谢您的回答。第一个EM处理不同的数据库,第二个EM处理另一个数据库,因此业务逻辑被分离。我正试图调试其他人代码上的一个bug,因此,对于正在做的事情,我真的没有太多要说的。若要解决实际问题,我是否应该实现
    getNewEntityManager($em)
    方法,并在
    $em1->persist
    $em2->persist
    之前调用它?你有什么建议?它会恢复EM并继续运行进程是否被关闭?考虑封闭的实体管理器是一个警报提示符,一些操作可能触发了一个低级别异常。我建议在持久化操作之前执行getNewEntityManager yes并调用目标刷新$em1->flush($entity)以避免链刷新其他实体。这方面运气好吗?我也有同样的问题。
    $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;
    }