【问题标题】:Symfony2 detach entity with ManyToOne associationSymfony2 分离实体与多对一关联
【发布时间】:2013-01-29 04:01:13
【问题描述】:

背景:

在我的应用程序中,我有一个具有自引用 ManyToOne 关联的实体(许多孩子可以指向一个单亲)。而且我有一项功能可以使用 Doctrine ORM 一次对许多实体进行大规模更新。为了防止由于加载了许多实体而导致性能急剧下降,我在更新实体后detach实体。

问题:

当我 detach 一个拥有 children 的实体之后尝试更新其中任何一个 children 时,Doctrine 抱怨它不知道 parent 了。即使我在尝试更新 child 之前merge parent 实体。

问题:

当我分离 parent 实体时我做错了什么?我尝试在父列上执行 cascade="merge" 和/或“分离”,当我尝试坚持时,Doctrine 仍然抱怨 parent 是一个未知实体。

我已经模拟了一个简单的例子来重现这一点。见下文。

测试代码:

Entity\Thing.php

/**
 * @ORM\Entity()
 * @ORM\Table(name="things")
 */
class Thing
{
    /**
     * @ORM\Id
     * @ORM\Column(type="integer")
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    protected $id;

    /**
     * @ORM\ManyToOne(targetEntity="Thing", inversedBy="children", cascade={"detach","merge"})
     * @ORM\JoinColumn(name="parentId", referencedColumnName="id", onDelete="SET NULL")
     */
    protected $parent;

    /**
     * @ORM\OneToMany(targetEntity="Thing", mappedBy="parent")
     */
    protected $children;

    /**
     * @ORM\Column(type="string", length=64)
     */
    protected $name;

    public function __construct($name = null)
    {
        $this->children = new ArrayCollection();
        $this->name = $name;
    }

    // .. SNIP ...
}

测试操作:

public function testThingAction($_route)
{
    $em = $this->getDoctrine()->getEntityManager();
    $repo = $em->getRepository('AcmeThingBundle:Thing');

    // simple setup of a couple things in the DB
    $t1 = $repo->findByName('Thing1');
    if (!$t1) {
        $t1 = new Thing('Thing1');
        $t2 = new Thing('Thing2');
        $t2->setParent($t1);

        $em->persist($t1);
        $em->persist($t2);
        $em->flush();
        return $this->redirect($this->generateUrl($_route));
    }

    list($t1, $t2) = $repo->findAll();

    // detach and re-merge Thing1
    // This should cause Thing1 to be removed and then re-added 
    // to the doctrine's known entities; but it doesn't!?
    $em->detach($t1);
    $em->merge($t1);

    // try to update T2
    $t2->setName('Thing2 - ' . time());
    $em->persist($t2);
    // will fail with: 
    // A new entity was found through the relationship Thing#parent
    $em->flush();

    return array();
}

【问题讨论】:

    标签: php symfony doctrine-orm symfony-2.1 cascade


    【解决方案1】:

    问题在于子对象与不再由 Doctrine 管理的特定父对象有关系。当您调用 $entityManager->merge($entity) 时,您会从该函数返回一个新的托管实体。

    当您取回它时,您需要手动调用 setParent() 以使用新管理的实体对您的每个孩子进行调用。

    【讨论】:

    • 你是对的 merge 返回一个新对象(通常我不需要重新合并实体;我只是在测试中这样做了)。但是,我认为这是 Doctrine 的一个潜在缺陷。因为一旦我分离父级,我就不能再对任何子级进行任何更改。任何人都知道为什么这是预期的行为?
    • 我不会把它称为教义上的缺陷。你要求它把孩子坚持给它不知道的父母。就学说而言,这样做会违反表中的外键约束(因为它无法确认实体不再有效)。我认为除了分离所有这些实体之外,您还应该担心其他优化。例如,将要保存的实体显式传递给 flush() 方法,这样教义就不会全部扫描它们。
    • 嗯,这确实有道理,让我意识到我专注于错误的实体来重新合并。我可以合并$t2,而不是尝试合并$t2,它可以级联到父级。我已经确认这有效。感谢您的输入。我并没有真正使用很多级联,我已经尝试了各种其他优化方式,但分离是我发现让我的导入功能正常工作的唯一方法,而不会减慢到每秒持续 1 次甚至更慢.
    猜你喜欢
    • 2015-08-10
    • 1970-01-01
    • 2018-10-09
    • 1970-01-01
    • 2010-12-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多