首页 > 解决方案 > 在 postRemove 侦听器中创建的实体未持久化

问题描述

我正在开发一个基于 Symfony 2.8 的项目,该项目基本上是一个在线通讯录。

AnEntityDeletionListener用于处理postRemove事件以将一些信息添加到删除日志中,这是一些内部簿记所需的:

class EntityDeletionListener implements EventSubscriber {
    public function getSubscribedEvents() {
        return array('postRemove');
    }

    public function postRemove(LifecycleEventArgs $args) {
        $entity = $args->getEntity();

        if (!$entity->shouldAddToDeleteLog())
           return ;

        $em = $args->getEntityManager();
        $repo = $em->getRepository('AppBundle:DeleteLog');

        $log = $repo->createEntity();
        $log->setGuid($entity->getGuid());
        ...

        $em->persist($log);

        // Flush entity manager if not disabled in entity settings
        if ($entity->shouldFlushDeleteLog())
            $em->flush();
    }
}

这没有任何问题:每次Contact删除实体时,都会自动创建删除日志条目。

现在我正在研究批量删除任意数量的选定Contacts. 为了加快进程,实体管理器不会在每次删除操作后刷新,而只会在处理一些批处理后刷新。

class ContactRepository extends EntityRepository {
    public function bulkDelete($guids) {
        $this->_em->getConnection()->beginTransaction();

        try {
            $batchSize = 100;
            $currentBatch = 0;           

            foreach ($guids as $guid) {
                $contact = $this->findOneByGuid($guid);

                if ($contact) {        
                    // DO NOT FLUSH EVERY DELETE LOG
                    $contact->setDeleteLogFlush(false);

                    $this->_em->remove($contact);

                    $currentBatch++;
                    if ($currentBatch % $batchSize === 0)
                        $this->_em->flush();
                }
            }

            $this->_em->flush();  
            $this->_em->getConnection()->commit();
        } catch (\Exception $ex) {            
            $this->_em->getConnection()->rollBack();
            return false;
        }
    }
}

问题 1: 如果我使用$contact->setDeleteLogFlush(false)避免自动刷新每个 DeleteLog,DeleteLogs则根本不会持久化。此外,Contact实体已正确删除,并且postRemove为每个删除操作执行,日志不会持久保存到数据库。

没有异常或其他错误可以解释为什么不保留日志。即使会出现错误,在这种情况下,完整的事务也会失败并且Contacts也不会被删除。但是他们被删除了...

问题 2: 如果不使用$contact->setDeleteLogFlush(false)侦听postRemove器刷新每个 new DeleteLog,一旦一次删除多个条目,我就会收到以下异常:

Uncaught PHP Exception Symfony\Component\Debug\Exception\FatalThrowableError: "Type error: Argument 2 passed to Doctrine\DBAL\Connection::delete() must be of the type array, null given

如何解决这个问题?postRemove为什么刷新实体管理器时监听器中创建的日志没有持久化?

标签: symfonydoctrine

解决方案


我认为问题在于您想要创建和删除多个Contact实体,但是您在错误的事件中执行此操作,因为如文档中所述

删除实体后,实体会发生 postRemove 事件。它将在数据库删除操作之后被调用。DQL DELETE 语句不调用它。

并且您不应该对数据库进行任何更改,如此处所述

postUpdate、postRemove、postPersist

这三个发布事件在 EntityManager#flush() 中调用。此处的更改与数据库中的持久性无关,但您可以使用这些事件来更改非持久性项目,例如非映射字段、日志记录,甚至是不直接由 Doctrine 映射的关联类。

所以我只是将整个postRemove逻辑转移到preRemove事件,因为

当每个实体传递给 EntityManager#remove() 方法时,都会在每个实体上调用 preRemove 事件。它对所有标记为级联删除的关联进行级联。


推荐阅读