我有一段时间怀疑,但现在是时候问一下了。请参阅下面的代码并在$someVar
中包含大量项目,例如200项:
// First approach
foreach($someVar as $item) {
$item = $em->getRepository('someEntity')->find($item['value']);
$em->remove($item);
$em->flush();
}
// Second approach
foreach($someVar as $item) {
$item = $em->getRepository('someEntity')->find($item['value']);
$em->remove($item);
}
$em->flush();
使用真实案例进行测试
在答案中提供了很好的信息,我仍然怀疑。看看这段代码:
foreach ($request->request->get( 'items' ) as $item) {
$items = $em->getRepository( 'AppBundle:FacturaProductoSolicitud' )->find( $item['value'] );
try {
$em->remove( $items );
$em->flush();
array_push($itemsRemoved, $item['value']);
$response['itemsRemoved'] = $itemsRemoved;
$response['success'] = true;
} catch ( \Exception $e ) {
dump( $e->getMessage() );
array_push($itemsNonRemoved, $item['value']);
$response['itemsNonRemoved'] = $itemsNonRemoved;
$response['success'] = false;
}
}
我在这里使用try {} catch() {}
句子,因为我需要知道删除了哪个$item['value']
才能将其添加到正确的数组中(请参阅$itemsRemoved
和{{1还有$itemsNonRemoved
为每个循环执行,我知道不好的做法,但是,从foreach循环中取出flush()
并在try-catch中执行,是否有任何方法可以获得flush
被删除了吗?怎么样?
答案 0 :(得分:7)
实际上,每次删除后运行flush()都是反模式。理想情况下,您应该在所有查询结束时运行一次。
在大多数情况下,Doctrine 2已经为您处理了正确的事务划分:所有写操作(INSERT / UPDATE / DELETE)都排队,直到调用EntityManager #flush(),将所有这些更改包装在一个事务。
但是,如果您想要更高的一致性,可以将查询包装在事务中。事实上,正如你可以在其best practices中阅读的那样,Doctrine鼓励它。
这两个电话都会这样做吗?意味着从数据库中删除记录?
是的,虽然在实体上调用remove不会导致在数据库上立即发出SQL DELETE。在下一次涉及该实体的EntityManager#flush()调用时,将删除该实体。这意味着仍可以查询计划删除的实体,并将其显示在查询和集合结果中。
因此,在循环内部刷新意味着大量SQL查询和对数据库的访问,并且实体将立即被删除。
在循环外部刷新意味着Doctrine执行了一个有效的事务(对数据库的一次访问),但实际上删除了实体,直到调用了flush。实体只会被标记为已删除。
在性能水平,哪一个最好用? (学说有时表现为记忆杀手)
毫无疑问,在循环外冲洗。这也是最好的做法。在某些情况下,每次持久化/删除/更新实体时,您确实需要执行刷新,但很少。
如果两种方法都很好,我是否也可以使用相同的每个UPDATE查询?
同样适用于更新/持久性。尽量避免冲洗内部循环。
总结一下,看看documentation。这真的很好解释。
如果由于某种原因导致任何查询失败,我如何抓住哪一个?可能是Doctrine
给出的错误您始终可以将flush
包装在try / catch块中,并优雅地捕获查询失败时抛出的异常。
try {
$em->flush()
}(\Exception $e) {
// do stuff
throw $e;// re-thrown Exception
}
当使用隐式事务划分并且在EntityManager #flush()期间发生异常时,事务将自动回滚并且EntityManager关闭。
有关here主题的更多信息。
<强>更新强>
在您提供的代码中,如果您在循环外使用flush,则所有删除操作都属于同一事务。这意味着如果它们中的任何一个失败,则抛出异常并发出回滚(执行的所有已删除操作都将被回滚,因此不会保留在DB上)。
例如:假设我们有四个带有id 1,2,3,4,5,6的项目,并假设删除4个失败。
第一个选项 - &gt;刷新内部循环:1,2,3被删除。 4失败抛出异常并结束。
第二个选项 - &gt;刷新外部循环:4次失败,回滚,没有删除,程序结束。
如果您要实现的行为是案例1中显示的行为,则可以选择一个您正在使用的行为。但是,它在性能方面确实很昂贵。
然而,有更好的解决方案:例如,您可以使用preRemove / postRemove事件的组合来存储在刷新中成功删除的那些实体的ID(或任何您想要的值)(尽管不是持久的,因为回滚)。例如,您可以将它们存储在属于该类的静态数组中(或使用单例或其他)。然后在异常的catch子句中,您可以使用该数组迭代并对这些项执行删除操作(当然,在循环外部刷新)。
然后,您可以返回数组,以便让用户知道您实际删除了这些实体,并且因为删除过程中存在问题而导致错误。