Skip to content

Loading…

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

Closed
doctrinebot opened this Issue · 10 comments

2 participants

@doctrinebot

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 movieartist and the SQL log shows DELETE FROM Movie_Artist WHERE Movieid = '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.

@doctrinebot

Comment created by @beberlei:

Can you explain why you merge $m1 twice?

@doctrinebot

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

Comment created by @beberlei:

Fixed

@doctrinebot

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

Comment created by @beberlei:

There is no CASCADE=MERGE on this relation or?

@doctrinebot

Comment created by ccapndave:

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

@doctrinebot

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

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

Issue was closed with resolution "Fixed"

@doctrinebot

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 :)

@beberlei beberlei was assigned by doctrinebot
@doctrinebot doctrinebot added this to the 2.0-BETA4 milestone
@doctrinebot doctrinebot closed this
@doctrinebot doctrinebot added the Bug label
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.