Doctrine orm 发生OptimisticLockException时,如何避免关闭EntityManager?

Doctrine orm 发生OptimisticLockException时,如何避免关闭EntityManager?,doctrine-orm,transactions,optimistic-locking,Doctrine Orm,Transactions,Optimistic Locking,我的问题-进程尝试更改已更改且具有最新版本id的实体。当我在UnitOfWork的commit()中的代码中执行flush()时,将引发OptimisticLockException并通过catch all块在同一位置捕获。在这个捕获理论中,关闭实体管理器。 如果要跳过此实体并继续使用ArrayCollection中的另一个实体,则不应使用flush() 尝试重新创建EntityManager: }catch (OptimisticLockException $e){ $this->

我的问题-进程尝试更改已更改且具有最新版本id的实体。当我在UnitOfWork的commit()中的代码中执行flush()时,将引发OptimisticLockException并通过catch all块在同一位置捕获。在这个捕获理论中,关闭实体管理器。 如果要跳过此实体并继续使用ArrayCollection中的另一个实体,则不应使用flush()

尝试重新创建EntityManager:

}catch (OptimisticLockException $e){
    $this->em = $this->container->get('doctrine')->getManager();
    echo "\n||OptimisticLockException.";
    continue;
}
而且仍然得到

[Doctrine\ORM\ORMException]   
The EntityManager is closed.
奇怪

如果我这样做

$this->em->lock($entity, LockMode::OPTIMISTIC, $entity->getVersion());
然后执行flush()我得到OptimisticLockException和closed entity manager。 如果我这样做

未使用此实体管理器注册旧数据,我甚至无法在数据库中写入日志,我收到错误:

[Symfony\Component\Debug\Exception\ContextErrorException]  
Notice: Undefined index: 00000000514cef3c000000002ff4781e

您应该在尝试刷新实体版本之前检查它,以避免异常。换句话说,如果锁失败,您不应该调用flush()方法

您可以使用
EntityManager#lock()
方法检查是否可以刷新实体

/**@var EntityManager$em*/
$entity=$em->getRepository('Post')->find($_请求['id']);
//获取预期版本(最简单的方法是将版本号作为隐藏表单字段)
$expectedVersion=$_请求['version'];
//更新您的实体
$entity->setText($_请求['text']);
试一试{
//断言您编辑了正确的版本
$em->lock($entity,LockMode::OPTIMISTIC,$expectedVersion);
//如果$em->lock()失败,则不会调用flush(),也不会关闭EntityManager
$em->flush();
}捕获(乐观锁定例外$e){
echo“抱歉,其他人已经更改了此实体。请重新应用更改!”;
}

查看条令文档中的示例

不幸的是,近4年后,条令仍然无法正确地从乐观锁定中恢复

如果数据库被其他服务器或php工作线程更改,则使用文档中建议的
lock
函数将不起作用。
lock
函数仅确保当前php脚本不会更改版本号,因为实体已加载到内存中。它不会读取数据库以确保版本号仍然是预期的版本号

即使它确实读取了数据库,在
lock
功能检查数据库中的当前版本和执行刷新之间,仍然可能存在争用条件

考虑以下情况:

  • 服务器A读取实体
  • 服务器B读取相同的实体
  • 服务器B更新数据库

  • 服务器A更新数据库,否则我必须在我的代码中的catch块中重新打开EntityManager?添加有问题的信息。我在文档中读到过,我理解这一点。我不想要回显“抱歉”信息,停止工作。我得到了很多数据,我不想再次请求所有数据,因为只有一个实体。在捕捉块中,EntityManager关闭。好的,我可以重置它并重新创建。但是所有数据都未在new EntityManager中注册,我无法再处理这些数据。在我的示例中,我只对一个实体执行了lock()检查,但在您的示例中,您需要对集合中的所有实体执行检查。如果某个实体的锁定失败,您需要决定如何解决它(从数据库重新加载实体、警告用户等)。当任何实体都没有通过锁检查时,不应该调用flush()。这并不像看上去那么容易。当抛出*LockException时,将关闭实体管理器以避免意外的数据不一致。没有一种通用的方法可以从中恢复,因为这取决于您的用例,并且您可以使用不同的策略来恢复,例如重新读取、应用更改、覆盖更改、放弃更改、通知用户、重新启动bacth脚本等。这就是无法在库中实现从中恢复的原因
    [Symfony\Component\Debug\Exception\ContextErrorException]  
    Notice: Undefined index: 00000000514cef3c000000002ff4781e