在学说-mongodb 中的 preUpdate 事件时创建/保留新文档

Posted

技术标签:

【中文标题】在学说-mongodb 中的 preUpdate 事件时创建/保留新文档【英文标题】:Creating/persisting new document while preUpdate event in doctrine-mongodb 【发布时间】:2014-07-18 15:26:27 【问题描述】:

我正在使用doctrine-mongodb-odm-1.0.0-BETA10 并尝试在preUpdate 事件运行时提供一些基于\InitialDocument 的自定义逻辑。

假设\InitialDocument 获得了一些状态,该状态必须作为新\StateDocument 的初始状态。我正在做这样的事情:

class InitDocListener implements \Doctrine\Common\EventSubscriber 
    public function getSubscribedEvents()
    
        return [
            Events::preUpdate
        ];
    

    public function preUpdate($args)
        $document = $args->getDocument();
        if($document instanceOf InitialDocument && $document->getState() == 'mine')
            $stateDocument = new \StateDocument();
            $stateDocument->setInitDocument($document);
            $args->getDocumentManager()->persist($stateDocument);
            //no flush cause recursion happens
        
    


prePersist 事件由\StateDocument 发生,但它不会在数据库中保留新文档。并且 postPersist 事件将永远不会被触发。

还有一些自定义逻辑,但都在事件范围内。在某些时候,逻辑可能会抛出一个异常,该异常必须停止 InitialDocument 的更新事件,因此 InitialDocument 状态取决于业务范围内的 \StateDocument 创建过程。

我该如何解决这个问题? preFlush 在 changeSet 重新计算之前运行的事件不能确定 InitialDocument 实例。因此,在preFlush 上“搜索”更新是一种技巧,让我认为这不是正确的方法。请给我适当的建议。谢谢。

【问题讨论】:

【参考方案1】:

我为您的用例 here 创建了一个测试用例。从您的问题中的代码中脱颖而出的一件事是,您没有在生命周期回调期间对您正在修改的文档调用recomputeSingleDocumentChangeSet(),正如preUpdate documentation 中提到的那样。但即使使用该调用,也不会插入新文档。这是因为 UnitOfWork 在插入和更新插入之后执行更新。完整订单可见UnitOfWork's commit() method:

文档更新 文档插入 文档更新 额外更新(由持久化类在内部安排) 集合删除 收藏更新 文档删除

preUpdate 事件被调度时,新文档的更新插入/插入已经发生。即使调用recomputeSingleDocumentChangeSet(),您最终也会安排要插入的文档,但UnitOfWork 会忽略这一点,并最终在清除所有计划队列here 时取消设置。

虽然一个简单的解决方案是让 ODM 在处理更新后检查其他插入,但在某些情况下这可能会导致无限循环。 UnitOfWork 排序早于我在项目上的工作,但我在构思最初的实现时可能会担心循环的风险。

作为一种变通方法,您可能希望让侦听器转储要插入到其他容器(或侦听器本身)的新文档,然后在事后检查要持久/刷新的其他文档。

【讨论】:

谢谢。昨天我在 UnitOfWork 上玩了一段时间,得出了类似的结论。您刚刚确认了在这种情况下我们需要在其他容器中处理插入文档的假设。问题在于 preUpdate 事件导致新文档的 MANAGED 状态在当时持续存在,并且在提交 //Clear up 其 upsertions(和其他内容)之后持续存在。文档保持已知并声明为已管理,但在该文档的新持久/刷新期间会导致错误。 所以我尝试包装 flush() 来处理自己的自定义事件 afterCommit(在 clear up 之后)并再次添加 scheduleForUpsert 以防止执行 persist()。但它没有用。现在我正试图完全放弃preUdpate,转而支持我的afterCommit。如果问题将被批准进入 GitHub,将带来一些(故障转移?)测试。 毕竟。决定放弃 preUpdate 事件以支持包装刷新并实现 afterCommit 事件并在新的文档持久性应用程序逻辑发出失败时具有回滚行为。队列: 1. preUpdate 验证初始文档并为回滚进行更改集。 2.afterUpdate 将文件附加到updated_docs collection。 3 afterCommit 事件触发基于updated_docs_collection 创建新文档并刷新它们或在失败时回滚更新的文档。不是一个好的实现,但现在可以工作。

以上是关于在学说-mongodb 中的 preUpdate 事件时创建/保留新文档的主要内容,如果未能解决你的问题,请参考以下文章

SonataAdminBundle 中的 Symfony preUpdate 事件

hibernate/JPA 中的 @PreUpdate 和 @Prepersist(使用会话)

Hibernate @PreUpdate:检查已更改的内容

无法在学说 mongo 查询中增加值

在 Symfony 2.1 中为 preUpdate 调用添加额外的持久化调用

@PrePersist 运行时,@PreUpdate 是不是总是运行?