mirror of
https://github.com/doctrine/orm.git
synced 2026-03-23 22:42:18 +01:00
Flushing one entity flushes also changes done in not related PersistentCollection #5789
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Originally created by @simara-svatopluk on GitHub (Nov 29, 2017).
Originally assigned to: @Ocramius on GitHub.
Scenario:
The problem is that PersistentCollection manipulate directly UnitOfWork and changes are flushed even if some another entity is flushed.
Test:
200f659d68Important is line 40; fails on 49
@Ocramius commented on GitHub (Nov 29, 2017):
flush($foo)is a performance optimisation, not a restriction. Callingflush($foo)is:$foo, whilst still saving changes computed so far@simara-svatopluk commented on GitHub (Nov 29, 2017):
Thank for an extremely fast response!
Am I able to determine which changes will be persisted and which not? Because detach is (or will be) deprecated too.
The whole use-case is that I'm implementing Repositories from DDD, and in one repository I assume that only one aggregate will be written to database/storage. I do not assume that the whole system state will be persisted.
@Ocramius commented on GitHub (Nov 29, 2017):
It is possible to check that during the
onFlushevent, but I suggest avoiding going down that road.Then you will need an
EntityManagerinstance per repository ;-)@lcobucci commented on GitHub (Nov 29, 2017):
Just to make sure... you're not flushing in the repository, right?
@simara-svatopluk commented on GitHub (Nov 29, 2017):
@lcobucci yep, I do. Do You suggest a better place?
But repository... it's not Doctrine Repository, it is implementation of an interface
@simara-svatopluk commented on GitHub (Nov 29, 2017):
This wouldn't help either because all instances of a class will be flushed.
@lcobucci commented on GitHub (Nov 29, 2017):
@simara-svatopluk IMO repositories should only call
persist()orremove()(for writing to the DB, sure), the flushing should be controlled by an upper layer (the one that handles the transactional part of your software). For example, I use tactician to handle commands/queries and I have a middleware which callsEntityManager#flush()for me after everything is done.But regardless of the architecture of your software, the idea of
EntityManager#flush()is to commit the unit of work - which means I'm done manipulating my objects for a certain operation, please send everything you got to the database.And finally, beware: if you're doing multiple flushes for a single operation (a command in my example) and you're not controlling DB transactions explicitly you'll definitely have partial rollbacks when things go wrong (which is not really nice for your DB consistency).
@simara-svatopluk commented on GitHub (Nov 29, 2017):
@lcobucci You made me think, thanks! I'll start from the end...
Transactions - You are right, sometimes inter-aggregates consistency is required.
I understand that
EntityManager#flush()means - And now save everything that is handled by UnitOfWork to the database.And now the most difficult - what is a repository?
For me, a repository is an abstract drawer (interface). I can look for a given entity by identity and entity can be there or not
function get($id). I can add a new entity to this abstract drawerfunction add($entity]. And finally when I find a given entity, I have it in my hands, I can modify this entity and then put it back to the drawerfunction save($entity)Since repository is an interface, it has to be implemented somehow, I've already worked with Doctrine, Elasticsearch, Cookie, Redis implementation. So an interface is pretty useful because I don't know the implementation in a domain layer. The problem is with transactions - not all technologies support transactions, but still, we can have a middleware/transaction manager that handle this.
And now all problems together - if I have an entity in my hands, I modify it, but I do not put it back to the drawer, shall it be in fact saved to any database?
Why? When a use-case involves more aggregates, usually I want to save only one. But other aggregates still support changing operations, I can call them accidentally (because I have no idea they change state), and when I call
flush(), I have no idea and no control on what was persisted to the database and what was not.What is a repository for You?
@simara-svatopluk commented on GitHub (Feb 5, 2018):
@lcobucci Thanks for Your point of view. I read more about DDD repositories and now I can only agree that their responsibility is not
save. Repositories supplement in-memory collections that uses references to objects, and there is no save use case.