【问题标题】:Reset all UnitOfWork changes in Doctrine重置 Doctrine 中的所有 UnitOfWork 更改
【发布时间】:2020-01-07 17:38:25
【问题描述】:

我正在寻找一种将 UnitOfWork 恢复到干净状态的方法,而无需清除整个事情。我仍然想访问我已加载的实体。这是我所拥有的基础,但我不确定这是否会以奇怪的方式破坏教义。

$uow = $em->getUnitOfWork();
$uow->computeChangeSets();//Can this be called once here, or should it be called in the inside loop so we get fresh information after a potential cascading refresh or persist?
foreach ($uow->getIdentityMap() as $className => $entities) {
    foreach ($entities as $oid => $entity) {
        if ($uow->isEntityScheduled($entity)) {
            if ($uow->isScheduledForInsert($entity)) {
                $uow->scheduleForDelete($entity);//Removes from the insert list without cascading delete or calling removal events
            } elseif ($uow->isScheduledForUpdate($entity)) {
                $em->refresh($entity);
            } elseif ($uow->isScheduledForDelete($entity)) {
                $em->persist($entity);
                $em->refresh($entity);//Should I re-call computeChangeSets() here so I can check if the entity is now scheduled for update?
            }
        }
    }
}

对于上下文:我正在运行批处理操作,在处理每条记录后,我会保留任何需要的更改。如果一条记录因错误而失败,则会处理该失败,但它可能会因此类异常而中断批处理的其余部分:

A new entity was found through the relationship FOO#bar that was not configured to cascade persist operations for entity: otherFooBar .

由于如果记录失败,我不需要保存处理记录的任何更改,因此我想简单地清理所有更改并保持对象图完整,同时继续处理批处理。

我已经阅读了教义的源代码,看起来这应该可以正常工作,但我对 Doctrine 的内部结构并不十分熟悉。有没有人看到或知道这种方法的缺陷/陷阱,或者知道实现此目的的更好方法?

编辑:澄清这个问题背后的原因。

当处理记录时发生错误时,这可能会使 UoW / EntityManger 处于无法在不被清除的情况下继续存在的位置。在我的特定情况下,错误使现有的已更改实体具有链接非持久实体,其中关系未配置为级联持久,这导致所有后续持久失败。此时,如果我想继续处理批处理并跳过失败的记录,我有两个选择:

1) 清除 EntityManager 并从数据库中重新加载所有业务逻辑实体。但这意味着恢复功能不能是通用的,必须自定义才能知道每次更改时要重新加载和调整哪些实体。

2) 使用 UnitOfWork 的现有功能清理 EntityManager,这允许代码是通用的并且不需要持续修改。

【问题讨论】:

  • 我已经通过refresh()ing 循环中更改的实体“解决”了这个问题。仍然容易出错,因为如果您在主循环中编辑代码,您可能还必须添加/删除/更新清理代码(即代码,可能在 catch() 子句中,它执行refresh())。这种耦合不是很明显。我很惊讶 Doctrine 本身没有任何回滚。
  • @hbogert 如果您查看问题中的示例代码,我确实对计划更新或删除的实体使用刷新。我认为刷新不适用于计划持久化的新实体。

标签: php symfony doctrine-orm


【解决方案1】:

您在示例代码中所做的问题在于您使用的是标记为内部的方法。这些并不是真正供“用户空间”代码使用的。如果您想安全地执行此操作,那么最好的选择实际上就是清除 EntityManager 并重新开始。

也就是说,您正在做的事情看起来应该有效。您无需调用 scheduleForDelete。只需在计划插入的实体上调用 detach,它将从 UnitOfWork 中删除。

【讨论】:

  • 清除实体管理器的问题是我正在尝试从错误中恢复。发生错误时会加载一些实体,但代码仍将存在并且不会与 EntityManager 分离。我读到的关于“computeChangeSets”的所有内容都表明自己调用它会破坏持久行为。因为我试图阻止图表中的任何新项目持续存在,所以这不是问题。 stackoverflow.com/a/16554133/1153227
  • 我不太了解您的用例。您是在尝试处理在 Doctrine 的持续周期中发生的错误,还是在处理应用程序中其他地方的错误?
  • 我正在处理一个与原则无关的错误,该错误使 EntityManager 处于导致所有未来错误的状态,直到它被清除。我已经编辑了我的问题,并进一步说明了我为什么要以这种方式解决问题。
【解决方案2】:

当您在实体上分离时,只需分离该实体,但对象管理器可能包含更多托管对象,这些对象将在刷新时持久保存到数据库,因此您应该使用

 /**
     * Clears the ObjectManager. All objects that are currently managed
     * by this ObjectManager become detached.
     *
     * @param string|null $objectName if given, only objects of this type will get detached.
     *
     * @return void
     */
$this->manager->clear();

【讨论】:

  • 我特别声明我不想分离所有托管对象,我只是想完全重置工作单元。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-02-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-06-05
相关资源
最近更新 更多