Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Persisting entities with OneToOne and OneToMany/ManyToOne relationship between them. #6499

@Frikkle

Description

@Frikkle

Last week I encountered an issue in one of our projects where we had two entities, call them A and B having a unidirectional OneToOne A->B (with the join column not nullable) and a OneToMany(A->B)/ManyToOne(B->A) bidirectional relationship between them.

The problem lies in the order of persisting them; when first persisting A, then B, a constraint violation is thrown: SQLSTATE[23000]: Integrity constraint violation: 19 NOT NULL constraint failed: a.b_id, indicating that the join column isn't allowed to be NULL, even though it should have been set.

I reworked my code to a minimal example:

Entity A

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

    /**
     * @ORM\OneToMany(targetEntity="AppBundle\Entity\B", mappedBy="a", cascade={"persist", "remove"}, orphanRemoval=true)
     */
    private $bs;

    /**
     * @ORM\OneToOne(targetEntity="AppBundle\Entity\B", cascade={"persist"})
     * @ORM\JoinColumn(nullable=false)
     */
    private $b;

    /**
     * @return int
     */
    public function getId()
    {
        return $this->id;
    }

    /**
     * @return B[]|ArrayCollection
     */
    public function getBs()
    {
        return $this->bs;
    }

    /**
     * @param B $b
     */
    public function addB(B $b)
    {
        if ($this->bs->contains($b)) return;

        $this->bs->add($b);

        // Update owning side
        $b->setA($this);
    }

    /**
     * @param B $b
     */
    public function removeB(B $b)
    {
        if (!$this->bs->contains($b)) return;

        $this->bs->removeElement($b);

        // Not updating owning side due to orphan removal
    }

    /**
     * @return B
     */
    public function getB()
    {
        return $this->b;
    }

    /**
     * @param B $b
     */
    public function setB(B $b)
    {
        $this->b = $b;
    }
}

Entity B

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

    /**
     * @ORM\ManyToOne(targetEntity="AppBundle\Entity\A", inversedBy="bs", cascade={"persist"})
     */
    private $a;

    /**
     * @return int
     */
    public function getId()
    {
        return $this->id;
    }

    /**
     * @return A
     */
    public function getA()
    {
        return $this->a;
    }

    /**
     * @param A $a
     */
    public function setA(A $a)
    {
        $this->a = $a;
    }
}

My persist code

$a = new A();
$entityManager->persist($a);

$b = new B();
$a->setB($b);
$entityManager->persist($b);

$entityManager->flush();

The code above throws the constraint violation error. At first I thought it might be related to circular cascade persists. But if I remove all cascade annotation and the orphanRemoval clauses as well, it still throws the ConstraintViolationException.

What does work?
It does however seem to work when I revert the persist order, like this:

$a = new A();
$b = new B();
$a->setB($b);

$entityManager->persist($b);
$entityManager->persist($a);

$entityManager->flush();

And, it also works when wrapping the whole operation in a transaction. But somehow I have the feeling that it should work.

Possibly related issues
When looking through the issue list I came across #4090 which seems to be a similar issue. Maybe this helps to shine light on that as well?

Our environment
We're working in Symfony 2.8 LTS using the doctrine/doctrine-bundle version 1.6.4 and doctrine/orm version 2.5.5. After upgradring both of them to the latest version, the problem persisted.

Metadata

Metadata

Assignees

Labels

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions