DDC-729: When merging many to many entities back into the repository all associations are deleted on the next flush #901

Closed
opened 2026-01-22 12:54:41 +01:00 by admin · 10 comments
Owner

Originally created by @doctrinebot on GitHub (Jul 31, 2010).

Originally assigned to: @beberlei on GitHub.

Jira issue originally created by user ccapndave:

When merging a DETACHED entity into the repository with a ManyToMany association, the entries in the join table are deleted on the next flush.

Many Movies have many Artists:

class Movie {

    /*** @Id @Column(type="integer") @GeneratedValue(strategy="IDENTITY") **/
    public $id;

    /*** @Column(length=50, type="string") **/
    public $title;

    /**** 
     * @ManyToMany(targetEntity="Artist")
     */
    public $artists;

    public function **construct() {
        $this->artists = new ArrayCollection();
    }

}
class Artist {

    /*** @Id @Column(type="integer") @GeneratedValue(strategy="IDENTITY") **/
    public $id;

    /*** @Column(length=50, type="string") **/
    public $name;

    /*** @ManyToMany(targetEntity="Movie", mappedBy="artists") **/
    public $movies;

    public function **construct() {
        $this->movies = new ArrayCollection();
    }

}

Assume that the database contains:
Movie: id=1, title="Movie 1"
Artist: id=1, name="Artist 1"

and that there is a entry (1, 1) in movie_artist so that there is a many-many relationship between Movie 1 and Artist 1.

I then run the following code to merge the existing associations into the entity manager:

$m1 = new \vo\Movie();
$m1->id = 1;
$m1->title = "Movie 1";

$a1 = new \vo\Artist();
$a1->id = 1;
$a1->name = "Artist 1";

$m1->artists->add($a1); $a1->movies->add($m1);

$m1 = $em->merge($m1);
$m1->artists->set(0, $em->merge($a1));
$a1->movies->set(0, $em->merge($m1));

At this point $m1 should contains merged entities reflecting the same as what is in the database.

If I now run:

$em->flush()

the association is deleted from movie_artist and the SQL log shows DELETE FROM Movie_Artist WHERE Movie_id = '1' as having been run.

Debugging $m1 both before and after the flush shows the expected values, and $em->getUnitOfWork()->computeChangeSets() is empty before the flush.

Originally created by @doctrinebot on GitHub (Jul 31, 2010). Originally assigned to: @beberlei on GitHub. Jira issue originally created by user ccapndave: When merging a DETACHED entity into the repository with a ManyToMany association, the entries in the join table are deleted on the next flush. Many Movies have many Artists: ``` class Movie { /*** @Id @Column(type="integer") @GeneratedValue(strategy="IDENTITY") **/ public $id; /*** @Column(length=50, type="string") **/ public $title; /**** * @ManyToMany(targetEntity="Artist") */ public $artists; public function **construct() { $this->artists = new ArrayCollection(); } } ``` ``` class Artist { /*** @Id @Column(type="integer") @GeneratedValue(strategy="IDENTITY") **/ public $id; /*** @Column(length=50, type="string") **/ public $name; /*** @ManyToMany(targetEntity="Movie", mappedBy="artists") **/ public $movies; public function **construct() { $this->movies = new ArrayCollection(); } } ``` Assume that the database contains: Movie: id=1, title="Movie 1" Artist: id=1, name="Artist 1" and that there is a entry (1, 1) in movie_artist so that there is a many-many relationship between Movie 1 and Artist 1. I then run the following code to merge the existing associations into the entity manager: ``` $m1 = new \vo\Movie(); $m1->id = 1; $m1->title = "Movie 1"; $a1 = new \vo\Artist(); $a1->id = 1; $a1->name = "Artist 1"; $m1->artists->add($a1); $a1->movies->add($m1); $m1 = $em->merge($m1); $m1->artists->set(0, $em->merge($a1)); $a1->movies->set(0, $em->merge($m1)); ``` At this point $m1 should contains merged entities reflecting the same as what is in the database. If I now run: ``` $em->flush() ``` the association is deleted from movie_artist and the SQL log shows DELETE FROM Movie_Artist WHERE Movie_id = '1' as having been run. Debugging $m1 both before and after the flush shows the expected values, and $em->getUnitOfWork()->computeChangeSets() is empty before the flush.
admin added the Bug label 2026-01-22 12:54:41 +01:00
admin closed this issue 2026-01-22 12:54:42 +01:00
Author
Owner

@doctrinebot commented on GitHub (Aug 8, 2010):

Comment created by @beberlei:

Can you explain why you merge $m1 twice?

@doctrinebot commented on GitHub (Aug 8, 2010): Comment created by @beberlei: Can you explain why you merge $m1 twice?
Author
Owner

@doctrinebot commented on GitHub (Aug 8, 2010):

Comment created by ccapndave:

Its a representation of how the algorithm I am using works - it assumes that once an entity is managed merge() will just return the already managed entity.

Anyway, to be sure I just ran another test that only calls merge() once, and it has the same behaviour.

@doctrinebot commented on GitHub (Aug 8, 2010): Comment created by ccapndave: Its a representation of how the algorithm I am using works - it assumes that once an entity is managed merge() will just return the already managed entity. Anyway, to be sure I just ran another test that only calls merge() once, and it has the same behaviour.
Author
Owner

@doctrinebot commented on GitHub (Aug 8, 2010):

Comment created by @beberlei:

Fixed

@doctrinebot commented on GitHub (Aug 8, 2010): Comment created by @beberlei: Fixed
Author
Owner

@doctrinebot commented on GitHub (Aug 9, 2010):

Comment created by ccapndave:

A slightly different many to many merge bug is occurring - I am re-opening this ticket for it because a) I expect it is somewhat related and b) the example to recreate follows on from the example in this bug.

Now assume the database has one more artist:

Movie: id=1, title="Movie 1"
Artist: id=1, name="Artist 1"
Artist: id=2, name="Artist 2"

and that there is still only an entry (1, 1) in movie_artist so that there is a many-many relationship between Movie 1 and Artist 1.

I then run the following code to merge the existing entity AND add a new association between artist 2 and movie 1:

$m1 = new \vo\Movie();
$m1->id = 1;
$m1->title = "Movie 1";

$a1 = new \vo\Artist();
$a1->id = 1;
$a1->name = "Artist 1";

$a2 = new \vo\Artist();
$a2->id = 2;
$a2->name = "Artist 2";

$m1->artists->add($a1); $a1->movies->add($m1);
$m1->artists->add($a2); $a2->movies->add($m1);

$m1 = $em->merge($m1);

$m1->artists->set(0, $em->merge($a1));
$a1->movies->set(0, $em->merge($m1));

$m1->artists->set(1, $em->merge($a2));
$a2->movies->set(1, $em->merge($m1));

If I now run:

$em->flush();

Instead of getting a (1, 2) entry added to movie_artist as expected, the change sets are empty and I get the following error:

Fatal error: Uncaught exception 'PDOException' with message 'SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry '1-2' for key 'PRIMARY'' in D:\Projects\ORM\doctrine2\lib\vendor\doctrine-dbal\lib\Doctrine\DBAL\Connection.php on line 646
( ! ) PDOException: SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry '1-2' for key 'PRIMARY' in D:\Projects\ORM\doctrine2\lib\vendor\doctrine-dbal\lib\Doctrine\DBAL\Connection.php on line 646
Call Stack

Time Memory Function Location

1 0.0006 400744 {main}( ) ..\index.php:0
2 0.0933 4375416 Doctrine\ORM\EntityManager->flush( ) ..\index.php:62
3 0.0934 4375416 Doctrine\ORM\UnitOfWork->commit( ) ..\EntityManager.php:320

@doctrinebot commented on GitHub (Aug 9, 2010): Comment created by ccapndave: A slightly different many to many merge bug is occurring - I am re-opening this ticket for it because a) I expect it is somewhat related and b) the example to recreate follows on from the example in this bug. Now assume the database has one more artist: Movie: id=1, title="Movie 1" Artist: id=1, name="Artist 1" Artist: id=2, name="Artist 2" and that there is still only an entry (1, 1) in movie_artist so that there is a many-many relationship between Movie 1 and Artist 1. I then run the following code to merge the existing entity AND add a new association between artist 2 and movie 1: ``` $m1 = new \vo\Movie(); $m1->id = 1; $m1->title = "Movie 1"; $a1 = new \vo\Artist(); $a1->id = 1; $a1->name = "Artist 1"; $a2 = new \vo\Artist(); $a2->id = 2; $a2->name = "Artist 2"; $m1->artists->add($a1); $a1->movies->add($m1); $m1->artists->add($a2); $a2->movies->add($m1); $m1 = $em->merge($m1); $m1->artists->set(0, $em->merge($a1)); $a1->movies->set(0, $em->merge($m1)); $m1->artists->set(1, $em->merge($a2)); $a2->movies->set(1, $em->merge($m1)); ``` If I now run: ``` $em->flush(); ``` Instead of getting a (1, 2) entry added to movie_artist as expected, the change sets are empty and I get the following error: Fatal error: Uncaught exception 'PDOException' with message 'SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry '1-2' for key 'PRIMARY'' in D:\Projects\ORM\doctrine2\lib\vendor\doctrine-dbal\lib\Doctrine\DBAL\Connection.php on line 646 ( ! ) PDOException: SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry '1-2' for key 'PRIMARY' in D:\Projects\ORM\doctrine2\lib\vendor\doctrine-dbal\lib\Doctrine\DBAL\Connection.php on line 646 Call Stack # Time Memory Function Location 1 0.0006 400744 {main}( ) ..\index.php:0 2 0.0933 4375416 Doctrine\ORM\EntityManager->flush( ) ..\index.php:62 3 0.0934 4375416 Doctrine\ORM\UnitOfWork->commit( ) ..\EntityManager.php:320
Author
Owner

@doctrinebot commented on GitHub (Aug 9, 2010):

Comment created by @beberlei:

There is no CASCADE=MERGE on this relation or?

@doctrinebot commented on GitHub (Aug 9, 2010): Comment created by @beberlei: There is no CASCADE=MERGE on this relation or?
Author
Owner

@doctrinebot commented on GitHub (Aug 9, 2010):

Comment created by ccapndave:

No - I think I am simulating what cascade merge would do though.

@doctrinebot commented on GitHub (Aug 9, 2010): Comment created by ccapndave: No - I think I am simulating what cascade merge would do though.
Author
Owner

@doctrinebot commented on GitHub (Aug 9, 2010):

Comment created by @beberlei:

I cannot reproduce this with any case.

Additionally your algorithm is REALLY slow compared to doing this natively in the UnitOfWork. The merge operation does lots of stuff even if the entity is already merged before.

@doctrinebot commented on GitHub (Aug 9, 2010): Comment created by @beberlei: I cannot reproduce this with any case. Additionally your algorithm is REALLY slow compared to doing this natively in the UnitOfWork. The merge operation does lots of stuff even if the entity is already merged before.
Author
Owner

@doctrinebot commented on GitHub (Aug 9, 2010):

Comment created by @beberlei:

Added 3 more tests that verify the correct behavior. Please add a failing test-case for this and open up a new ticket.

@doctrinebot commented on GitHub (Aug 9, 2010): Comment created by @beberlei: Added 3 more tests that verify the correct behavior. Please add a failing test-case for this and open up a new ticket.
Author
Owner

@doctrinebot commented on GitHub (Aug 9, 2010):

Issue was closed with resolution "Fixed"

@doctrinebot commented on GitHub (Aug 9, 2010): Issue was closed with resolution "Fixed"
Author
Owner

@doctrinebot commented on GitHub (Aug 11, 2010):

Comment created by ccapndave:

Thanks for pointing out the inefficiency in the algorithm - you are completely right and as recommended I have changed the Flextrine codebase to use UnitOfWork merge (by internally changing the metadata to enable cascade). However, there still seems to be a bug mergng many to many - I will open it up in a new ticket. Cheers :)

@doctrinebot commented on GitHub (Aug 11, 2010): Comment created by ccapndave: Thanks for pointing out the inefficiency in the algorithm - you are completely right and as recommended I have changed the Flextrine codebase to use UnitOfWork merge (by internally changing the metadata to enable cascade). However, there still seems to be a bug mergng many to many - I will open it up in a new ticket. Cheers :)
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: doctrine/archived-orm#901