symfony - 在 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
为什么刷新实体管理器时监听器中创建的日志没有持久化?
解决方案
我认为问题在于您想要创建和删除多个Contact
实体,但是您在错误的事件中执行此操作,因为如文档中所述
删除实体后,实体会发生 postRemove 事件。它将在数据库删除操作之后被调用。DQL DELETE 语句不调用它。
并且您不应该对数据库进行任何更改,如此处所述
postUpdate、postRemove、postPersist
这三个发布事件在 EntityManager#flush() 中调用。此处的更改与数据库中的持久性无关,但您可以使用这些事件来更改非持久性项目,例如非映射字段、日志记录,甚至是不直接由 Doctrine 映射的关联类。
所以我只是将整个postRemove
逻辑转移到preRemove
事件,因为
当每个实体传递给 EntityManager#remove() 方法时,都会在每个实体上调用 preRemove 事件。它对所有标记为级联删除的关联进行级联。
推荐阅读
- java - Java 和 .Net 4.6 为相同的密钥生成不同的 JSON Web 加密 (JWE) 令牌
- python - 如何动态同步并立即显示两个条目中的值
- r - 如何为每行中不等于最大值的那个赋值?
- python - 在烧瓶应用程序内测试“烧瓶数据库初始化”是否曾经运行过
- kubernetes - 如何检查我在 Kubernetes 中的所有部署是否已完成
- azure - 无法使用 Azure Cosmos DB 查看 READ 的统计信息和 WRITE 和 ALL 的错误统计信息
- c# - 根据查询结果勾选单选框列表项值
- python - 我缺少使用 Django 和 Python 编辑数据库记录的内容
- angular - rxjs/Observable:一个一个地执行函数,并将函数的参数作为前一个函数的结果传递
- python - 将 lsb 隐写图像转换为视频时丢失数据