[DDC-1974] @OneToOne and @InheritanceType cause Duplicate Key Errors when changing the entity Created: 09/Aug/12  Updated: 29/Aug/12  Resolved: 29/Aug/12

Status: Resolved
Project: Doctrine 2 - ORM
Component/s: ORM
Affects Version/s: 2.2.2
Fix Version/s: None
Security Level: All

Type: Bug Priority: Major
Reporter: Philipp Dobrigkeit Assignee: Benjamin Eberlei
Resolution: Won't Fix Votes: 0
Labels: commitorder
Environment:

Win 7, PHP 5.3, MySQL InnoDB



 Description   
<?php

/** @Entity */
class A
{
    /**
     * @Id @Column(name="id", type="integer")
     */
    private $id;

    /**
     * @OneToOne(targetEntity="B", mappedBy="a")
     */
    private $b;
}

/** 
 * @Entity 
 * @InheritanceType("SINGLE_TABLE")
 */
class B
{
    /**
     * @Id @Column(name="id", type="integer", nullable=false)
     */
    private $id;

    /**
     * @OneToOne(targetEntity="A", inversedBy="b")
     */
    private $a;
}

/** 
 * @Entity 
 */
class B_sub extends B
{
    /** @Column(type="string") */
    private $sub;
}

/** 
 * @Entity 
 */
class B_sub2 extends B
{
    /** @Column(type="string") */
    private $sub2;
}

The code creates several tables and a UNIQUE KEY constraint on B (a_id)

Now trying to accomplish the following sequence of action:

$B = new B_sub();
$A = new A();

$A->set('b', $B);
$B->set('a', $A);

$em->persist($A);
$em->persist($B);
//works fine and creates the correct DB entries
$em->flush();

$B_new = new B_sub2();
$B_old = $A->get('b');
$A->set('b', $B_new);
$B_new->set('a', $A);

$em->remove($B_old);
$em->persist($A);
$em->persist($B_new);
//does not work, because B_new is inserted first and fails the UNIQUE KEY constraint (both B_new and B_old refer to A)
$em->flush();

The result is a "Duplicate entry '1' for key 'UNIQ_7F6BFCEBE26CCE03'"



 Comments   
Comment by Philipp Dobrigkeit [ 10/Aug/12 ]

Current workaround is to change the ownership of the relationship

<?php

/** @Entity */
class A
{
    /**
     * @Id @Column(name="id", type="integer")
     */
    private $id;

    /**
     * @OneToOne(targetEntity="B", inversedBy="a")
     */
    private $b;
}

/** 
 * @Entity 
 * @InheritanceType("SINGLE_TABLE")
 */
class B
{
    /**
     * @Id @Column(name="id", type="integer", nullable=false)
     */
    private $id;

    /**
     * @OneToOne(targetEntity="A", mappedBy="b")
     */
    private $a;
}
Comment by Benjamin Eberlei [ 29/Aug/12 ]

Insert before Removal is by design, see discussion here http://www.doctrine-project.org/jira/browse/DDC-601





[DDC-1968] CommitOrderCalculator fails to calculate right order for mixing one-to-many and one-to-one relations Created: 05/Aug/12  Updated: 06/Jan/13  Resolved: 06/Jan/13

Status: Resolved
Project: Doctrine 2 - ORM
Component/s: ORM
Affects Version/s: 2.0.7
Fix Version/s: None
Security Level: All

Type: Bug Priority: Major
Reporter: miptpatriot Assignee: Benjamin Eberlei
Resolution: Invalid Votes: 1
Labels: commitorder


 Description   

I have 2 classes. Zone and Rotator.
Zone relates to Rotator as One-to-Many. And Zone COULD realte to Rotator as One-to-One. Here is code snippet:

 
class Zone
{
    /**
     * @var integer $id
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;

    /**
     * @ORM\OneToMany(targetEntity="Rotator", mappedBy="zone", cascade={"persist", "remove", "merge"}, orphanRemoval=true)
     * @ORM\OrderBy({"id" = "ASC"})
     */
    private $rotators = array();

    /**
     * @var Rotator $mainRotator
     *
     * @ORM\OneToOne(targetEntity="Rotator")
     * @ORM\JoinColumn(name="mainRotatorId", referencedColumnName="id")
     */
    private $mainRotator;

    /**
     * @var int $mainRotatorId
     *
     * @ORM\Column(name="mainRotatorId", type="integer", nullable="true")
     */
    private $mainRotatorId;
}
 
class Rotator
{
    /**
     * @var integer $id
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;

    /**
     * @var Zone $zone
     *
     * @ORM\ManyToOne(targetEntity="Zone")
     * @ORM\JoinColumn(name="zoneId", referencedColumnName="id")
     */
    private $zone;

    /**
     * @var int $zoneId
     *
     * @ORM\Column(name="zoneId", type="integer")
     */
    private $zoneId;
}

When I delete only Zone - calculator works fine. But it fails if I delete entity Site, that relates to Zone as one-to-many (and have a lot of other raltions, so I don't write it here).

As I look through the code, I see CommitOrderCalculator doesn't distiguish mandatory many-to-one relation from optional one-to-one one.

PS this code hadn't been changed since 2.0, so this bug should be reproducable for master.



 Comments   
Comment by Guilherme Blanco [ 17/Aug/12 ]

Your issue is because you have $zone and $zoneId pointing both to an association AND to a field.
Remove the $zoneId field (you can grab it by many different ways) and your issue should be solved.

Comment by Benjamin Eberlei [ 06/Oct/12 ]

You mention Site? Where is this entity, please make a snippet available as well.

And then, can you show some example object graph that you are trying to delete? This case is data dependent.

Comment by miptpatriot [ 06/Oct/12 ]
class Site
{
    /**
     * @var integer $id
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;

    /**
     * @var \Doctrine\Common\Collections\Collection|SiteAlias[]
     * @ORM\OneToMany(targetEntity="SiteAlias", mappedBy="site", cascade={"persist", "remove", "merge"}, orphanRemoval=true)
     */
    private $aliases;

    /**
     * @ORM\OneToMany(targetEntity="Zone", mappedBy="site", cascade={"persist", "remove", "merge"}, orphanRemoval=true)
     */
    private $zones;

    /**
     * @ORM\OneToMany(targetEntity="Banner", mappedBy="site", cascade={"persist", "remove", "merge"}, orphanRemoval=true)
     */
    private $banners;

    /**
     * @var integer
     *
     * @ORM\Column(name="user_id", type="integer")
     */
    private $user_id;

    /**
     * @var \Iw\UserBundle\Entity\User
     *
     * @ORM\ManyToOne(targetEntity="\Iw\UserBundle\Entity\User")
     * @ORM\JoinColumn(name="user_id", referencedColumnName="id")
     */
    private $user;
}
Comment by miptpatriot [ 06/Oct/12 ]

Is there any fast way to debug output object graph? Like execute print_r somewhere in code.

Comment by Ivan Borzenkov [ 08/Nov/12 ]

Confirmed
removing ID fields not work

i fix this whis add

  • @ORM\JoinColumn(onDelete="cascade")
    to child Entity ManyToOne link

OnetoOne Unidirectinal set child after of parent
but OneToMany set set child before of parent (how need)

Comment by Benjamin Eberlei [ 06/Jan/13 ]

I finally get this issue, and bidirecitonal foreign keys are only deletable with "onDelete=CASCADE", there is absolutely no way for the CommitOrderCalculator to fix this.





Generated at Wed Sep 17 15:49:55 UTC 2014 using JIRA 6.2.3#6260-sha1:63ef1d6dac3f4f4d7db4c1effd405ba38ccdc558.