Php foreach循环内部和外部的call flush()之间的区别,使用哪一个?

Php foreach循环内部和外部的call flush()之间的区别,使用哪一个?,php,symfony,doctrine-orm,Php,Symfony,Doctrine Orm,我对此有一段时间的怀疑,但现在是时候问一下了。请参见下面的代码,在$someVar中有大量项目,例如200个项目: // First approach foreach($someVar as $item) { $item = $em->getRepository('someEntity')->find($item['value']); $em->remove($item); $em->flush(); } // Second approach f

我对此有一段时间的怀疑,但现在是时候问一下了。请参见下面的代码,在
$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
$itemsNonRemoved
),而且
刷新()
对每个循环都执行,我知道这是一个糟糕的做法,但是,从foreach循环中取出
flush
,并在try-catch中执行,是否有任何方法可以获取已删除的
$item['value']
?如何操作?

实际上,每次删除后运行flush()都是一种反模式。理想情况下,应该在所有查询结束时运行一次

在大多数情况下,原则2已经为您提供了适当的事务划分:所有写入操作(插入/更新/删除)都将排队,直到调用EntityManager#flush(),将所有这些更改封装在一个事务中

但是,如果您想要更高的一致性,可以将查询包装在事务中。事实上,正如你在书中所读到的那样,它是受到教义的鼓励的

两个呼叫都将执行相同的操作?是否要从数据库中删除记录?

是,但对实体调用remove不会导致对数据库发出立即SQL删除。该实体将在涉及该实体的EntityManager#flush()的下一次调用中删除。这意味着计划删除的实体仍然可以查询并显示在查询和收集结果中

因此,在循环中刷新意味着大量SQL查询和对数据库的访问,实体将立即被删除

循环外刷新意味着一个由条令执行的高效事务(一次对数据库的访问),但在调用刷新之前,实体不会被实际删除。实体将仅标记为已删除

在性能级别,哪一个最适合使用?(条令有时会成为记忆杀手)

毫无疑问,冲洗出循环。这也是最佳实践。在某些情况下,您可能真的需要在每次持久化/删除/更新实体时执行刷新,但很少

如果两种方法都很好,我是否也可以使用相同的每次更新查询?

这同样适用于更新/持久化。不惜一切代价避免在回路内部冲洗

最后,请看一看。这真的解释得很好

如果任何查询因某种原因失败,我如何捕获哪一个?还有可能是条令中的错误

您可以始终将
flush
封装在try/catch块中,优雅地捕获查询失败时抛出的异常

try {
   $em->flush()
}(\Exception $e) {
    // do stuff
    throw $e;// re-thrown Exception
}
当使用隐式事务划分且EntityManager#flush()期间发生异常时,事务将自动回滚并关闭EntityManager

有关该主题的更多信息

更新

在您提供的代码中,如果在循环外使用flush,则所有删除操作都将属于同一事务。这意味着,如果其中任何操作失败,将抛出异常并发出回滚(执行的所有已删除操作都将回滚,因此不会在数据库上持久化)

例如:假设我们有四个ID为1、2、3、4、5、6的项,并假设删除4失败

第一个选项->刷新内部循环:删除1,2,3。4失败抛出异常并结束

第二个选项->刷新外部循环:4个失败,回滚,无一个被删除,程序结束

如果您想要实现的行为如案例1所示,那么一个选项可以是您正在使用的行为。然而,就性能而言,它确实很昂贵

尽管如此,还是有更好的解决方案:例如,您可以使用preRemove/postRemove事件的组合来存储在刷新中成功删除的实体的ID(或任何您想要的值)(尽管由于回滚而没有持久化)。例如,您可以将它们存储在属于该类的静态数组中(或者使用单例或其他方式)。然后在异常的catch子句中,可以使用该数组对这些项进行迭代并执行删除操作(当然,在循环外刷新)


然后,您可以返回数组,以便让用户知道您确实删除了这些实体,并且因为删除过程中出现问题而为false。

回答得很好,但仍有一些疑问,您可以查看我在主帖子上的编辑吗?@ReynierPM谢谢:)我已经更新了回答您问题的答案,希望它能再次帮助我找到令人惊讶的答案,但是你能不能给我一些关于你留给我的解释的代码?我不是Symfony专家,有些东西对我来说很模糊,但我仍在学习,你能吗?据我所知,那些大学预科生/大学预科生是生命周期回调的一部分?那么我是如何得到这些价值观的呢?此外,removedItems应该是从DB中删除的项,否则将没有意义,有任何建议吗?第一个选项是否可以选择:
first option->Flush inside loop:1,2,3被删除。4失败将引发异常并结束。
当3失败时,是否继续4?