-
Notifications
You must be signed in to change notification settings - Fork 933
Delay entity insert on Persist until session is flushed #1754
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
I could not decide if I'm fixing a bug or making an improvement. Duh. /cc @fredericDelaporte |
The JSR-220 states about
So an insert occurring out of a transaction and without an explicit flush can be considered a bug. Now, as you have already flagged, this could be a breaking change: maybe some people access the id after a persist even on I also wonder how well fare cases with dependencies: do we have tests for them? I mean, cases where we persist (and not save) entities referencing each others. Does NHibernate correctly retrieve the id in time for supplying adequate values to the foreign keys? And what about batching? Is batching enabled thanks to this change? It looks easily doable for cases where the id is retrieved as a result set field. But if it is retrieved as an output parameter, that could be more complicated. And finally, I wonder why, when we are inside a transaction, the insert should still happen immediately? (Apart from the facts: that it looks conform with the spec, which somehow allow the insert to happen anytime before commit; that it will not be permanent in case of explicit rollback or failure before commit.) |
No
Not so easy. I also thought that this would enable batching of inserts of entities with identity-like identifiers. Basically we have 2 batching techniques: one which does not require the output – So, to enable batching for server-side identifier we would need to rewrite the |
It's not a bug because you have written yourself:
So, technically what it says is that insert could happen any time before the |
It is a bug, because it is not written it can happen before a flush. What I have written does not state otherwise. It is is written it can happen "at or before a commit". Then the next "or" start a new proposition, especially since it is worded in a way which cannot syntactically well combine as an alternative of the first "or".
No. I understand it as saying it can happen at commit, or before commit, or at flush. Understanding it as meaning it can happen also before flush does look weird to me, due to the way that sentence is written. |
The flush mode is a problem here. As with |
This can be considered as an "optimization". Because transactions are atomic, the changes are fixed in the database at the commit. And it does not really matter when the I have ported HHH-11019 (sits locally), but I don't really understand why shall we care about flush mode when we are in a transaction. It does not really matter. I would say that it's better to be just this: bool shouldDelayIdentityInserts = !requiresImmediateIdAccess; This simple condition would promote the same behaviour regardless of the transactions. |
Yes but these are still flushes, and documented. I should not have written "explicit flush" anywhere indeed, the spec does not handle that point. Moreover the user can avoid these flushes if he wishes to. So I do not see these flush modes as a trouble. They are just a thing users should be aware of, but that is just not always the case.
Yes, that is somewhat what my following parenthesis was attempting to mean.
Agreed. I guess that on Hibernate side they have cared for flush mode in order to minimize the impact of the change. Maybe some kind of overcautious handling of changes. (They are doing the change for correctness of a pattern which can anyway not be correct with auto or always flush modes. So this aim does not require changing the behavior of identity inserts for these two flush modes. But that does not mean it is a good thing to keep that behavior.) |
I intend to add the changed proposed by @hazzik:
And then this PR would no more be WIP, the port of HHH-11019 being dropped as irrelevant to our needs. |
@fredericDelaporte done |
So, this does not work. I'm leaning towards dropping the last commit and leave as it was. |
Maybe there are more troubles then. Checking the But it does fail the same way on your branch, even without the last commit, when the transaction is removed. So that last change is, at least for this test, not the cause of the regression. There is a regression there even without that last change. |
Want to port https://hibernate.atlassian.net/browse/HHH-12826 and https://hibernate.atlassian.net/browse/HHH-12846 first |
To be squashed
To be squashed.
@fredericDelaporte I would like to drop |
This comment has been minimized.
This comment has been minimized.
[Obsolete("Please use GetKey() instead.")] | ||
protected internal object Key => GetKey(); | ||
|
||
protected object GetKey() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Making this a method enables to generate an async counterpart.
After some more considerations, indeed update Also, regards your previous comment, @fredericDelaporte. The exception is thrown because the code just prior |
@fredericDelaporte can you come up with some test cases? |
This comment has been minimized.
This comment has been minimized.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Test cases for ownership added. (I have checked, Hibernate fix fails some of them.)
Has reported by @hazzik in his first comment What about inside of transaction boundaries? I saw the bug introduced 15 years ago when we moved, quickly, NH forward to what was the current Hibernate version... and it was a SVN commit (lot of code in one shot) sorry for that, I'm guilty. The original code was:
Please make the FIX taking in account the transaction boundaries. |
The specification only cares about persist() being called outside of transaction boundaries. And it does not have a defined behavior when inside the transaction boundaries. So "we have another bug" is non sequitur:
A = persist() is called outside of transaction boundaries
Not executing INSERT while inside a transaction IS a valid behavior. In other words persist() can issue INSERT while inside a transaction, but not required to do so. If you look at commit history and discussion we were going back and forth between the original and current implementation. So, at the end we decided that actions from persist would be executed on flush. Which does complies to the statements above about the persist's behavior and it complies to JSR-220:
I'm not against the code which is in Hibernate, however "fixing it" requires a sacrifice on another well defined behavior, the behavior of |
@hazzik
should result in an Let me show you a succinct more realistic case:
In this case, a bunch of events may change the state of The advantage of the usage of BTW, I would avoid a discussion. I understood that this will be the new behavior from v5.3.x, I know where is the source problem and, inside my current work, the usage of Thanks to you all for your hard work with NHibernate. |
For centuries people did believe in a myth that
identity
always triggers immediate insert. I'll prove them wrong.Here is an explanation of how the
persist()
should work.In fact, it did work like that before 2.0.0.Alpha1. But here the assumption has been made that
shouldDelayIdentityInserts
is always false, however, this is not true. If we stick to the commented logicrequiresImmediateIdAccess
isfalse
on persist. AsPersist()
method hasvoid
return type and so does not require the ID.This earlier change devalued the
Persist()
and made it a weird little brother ofSave()
with an unclear purpose of existence.This is WIP currently and later it will contain the tests and port of HHH-11019 - delayed id generation ifFlushMode
isManual
orCommit
.Related to NH-3802.