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
- Add test to show why delete-before-insert may be challenging There are a few requests (#5742, #5368, #5109, #6776) that ask to change the order of operations in the UnitOfWork to perform "deletes bef... — committed to mpdude/doctrine2 by mpdude a year ago
- Add test to show why delete-before-insert may be challenging There are a few requests (#5742, #5368, #5109, #6776) that ask to change the order of operations in the UnitOfWork to perform "deletes bef... — committed to mpdude/doctrine2 by mpdude a year ago
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()
andadd()
to again. You can usually notflush()
in between, as the whole operation happens in your model, with no knowledge of theEntityManager
. 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:
One could argue that you could then do
UPDATE
,DELETE
, thenINSERT
. But if anUPDATE
needs anINSERT
, 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
beforeINSERT
, 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:
StagedItemCollection
andRemoval
,Update
,Insert
pro: clean api con: memory consumption (not sure it’s relevant with recent php changes), more changes[$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 uowIn 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.