orm: DDC-601: UnitOfWork commit order problem

Jira issue originally created by user exxbrain:

The next critical case doesn’t work, because of Duplicate entry error:

$obj = $repository->findOneByUniqueKey($key);

if ($obj != null) {
     $em->remove($obj);
}
$obj = new Object();
$obj->setUniqueKey($key);
$em->persist($obj);

$em->flush();

Instead of that i must use the next construction:

$obj = $repository->findOneByUniqueKey($key);

if ($obj != null) {
     $em->remove($obj);
     $em->flush();      //  the possible data inconsistency cause
}
$obj = new Object();
$obj->setUniqueKey($key);
$em->persist($obj);

$em->flush();

About this issue

  • Original URL
  • State: open
  • Created 14 years ago
  • Reactions: 7
  • Comments: 22 (6 by maintainers)

Commits related to this issue

Most upvoted comments

Even though this whole issue relates to the edge case of replacing entities with unique constraints with one another, “won’t fix” won’t make it go away. The current workarounds look horrifying, to say the least, and there really should be a better way to solve this problem.

Theoretically: As Doctrine knows about the unique constraints of entities it could automatically check if the current unit of work contains an entity (A) with unique constraints that gets replaced by another entity (B) of the same class and with the same values in the constrained fields. In that case it needs to delete A before inserting B and everything should be fine. Or am I missing something?

I’d like to emphasize that this really is a pain when you deal with collections, that you want to clear() and add() to again. You can usually not flush() in between, as the whole operation happens in your model, with no knowledge of the EntityManager. So if your table contains unique constraints, you’re screwed because the inserts are performed before the deletes. I’ve had this problem on pretty much every project I’ve used Doctrine on.

The explanation of the current order of operations lies in this link:

Deletes are general performed last, and an update may be required in order to allow deletion because of constraints.

One could argue that you could then do UPDATE, DELETE, then INSERT. But if an UPDATE needs an INSERT, you’re screwed.

So there is no one-size-fits-all solution to this problem.

What would be good is, indeed, a configuration option to force DELETE before INSERT, at least in a collection.

Comment created by deatheriam:

At least give us, users an option to use this approach:

{quote}Using the Unit of Work setShouldPerformDeletesFirst Method

By default, TopLink does insert and update operations first, before delete operations, to ensure that referential integrity is maintained. This is the preferred approach.

If you are forced to replace an object with unique constraints by deleting it and inserting a replacement, you may cause a constraint violation if the insert operation occurs before the delete operation. In this case, call setShouldPerformDeletesFirst to perform the delete operation before the insert operation.{quote}

Comment created by jean-gui:

Calling flush() after remove() is not always possible when using Doctrine with Symfony, because bindRequest does everything under the hood. A function “setShouldPerformDeletesFirst” as described in http://docs.oracle.com/cd/B31017_01/web.1013/b28218/uowadv.htm#i1137484 would be very useful in those cases. Without it, I need to mess with detach() and merge() to keep only the things I want to remove() attached, flush() and then merge() everything back before a second flush().

I was looking for a way to fix this issue because I think that it’s really an issue. (I’m new in this thread, and old reasons of why it will not be fixed are not accessible anymore x))

I think the best way to handle this is to change how are staged entities in the uow. They are currently simple arrays but to support order on a commit we need to add a new order notion. I have 2 ideas, with pro and cons:

  1. Using a new set of objects like StagedItemCollection and Removal, Update, Insert pro: clean api con: memory consumption (not sure it’s relevant with recent php changes), more changes
  2. Changing the way the array is composed for something like [$stagedOrder, $entity] instead of just the entity pro: little change, no change on performance con: will obviously lead to complex and more code inside the uow

In any case, it will lead to major changes deep inside the uow in many many functions.

I’m interested in trying something. I’d like to know if somebody would be able to follow me until it’s merged or if it looks more like a real wontfix (as I said, I cannot really read previous arguments of why it’s not a good idea to fix it, I’m open to anything).

Thanks!

Reopening for research only for now, 3.0 could be something where we rethink if that might be fixable.