Issues
2 of 2
Batched unidirectional one-to-many update failing
Created last week
Updated 2 days ago
Activity
Yashwanth2 days ago
We are currently upgrading to Hibernate 6.6.5 where we are facing a similar issue with a delete operation that throws an OptimisticLockException with the following stacktrace:
We are unsure how the Hibernate release schedule works but in our organization we still use hbm.xml, which is deprecated but not removed in 6.6.5. If there is a fix for this issue, will there be a backport? Since we are still in our upgrading phase and cannot directly move to 7.0 (where hbm.xml is removed), any suggestions would be greatly appreciated.
For a unidirectional one-to-many relation moving a "many" entity from a "one" entities' collection to another "one" can fail with a
StaleStateException
in batched mode if the SQL statements happen to be executed in the "wrong” order.Working example
Considering this mapping (simplified excerpt):
Running this code to move
phone
fromperson1
'sphones
collection toperson2
usually works:It executes these SQL statements both with and without batching and everything works fine:
update Phone set person_id=null where person_id=? and id=?
with?
bound to 1 and 1update Phone set person_id=? where id=?
with?
bound to 2 and 1Breaking example
However, if we re-order loading the
Person
entitiesThe SQL statement order changes to:
update Phone set person_id=? where id=?
with?
bound to 2 and 1update Phone set person_id=null where person_id=? and id=?
with?
bound to 1 and 1This still works fine in non-batched mode. This mode ignores the number of actually affected statements, which is 0 for the second statement (as the old
person_id
column value has already been replaced by the first statement).In batched mode we get an exception for the second statement instead:
For the SQL statements to look like this (and to fail),
person1.getPhones()
needs to contain anotherPhone
that stays there. See the reproduce for the full code.Reproducer
Analysis
One reason for this is that the two
Person
s'phones
collections are flushed independently of each other despite both referring to the same database row (for the onephone
). Depending on the execution order theupdate Phone set person_id=null ...
statement does not change anything,It seems changing the independent flushing approach is very hard considering the current implementation. To me it looks easier to make Hibernate ignore that in some cases statements have fewer affected rows than usual.
The flush order of the two collections is determined by the order the
Person
entities are loaded into the session (or by their ids when usinghibernate.order_updates
).The exception happens in Hibernate 6.6 (and at least 6.4), but no longer in 7.0/main. The behavior changed as a side-product of , which now swallows the
StaleStateException
in this case.I'm not sure this behavior change was intentional and can serve as the basis for a fix in 6.6. I will add a comment in that ticket to discuss this.
If it was not intentional, it can make sense to use
org.hibernate.jdbc.Expectation.None
to ignore the number of affected rows for specific statements.Non-batched mode is not affected as DeleteRowsCoordinatorStandard.deleteRows passes a
null
OperationResultChecker
to the actual SQL execution, ignoring the number of affected rows.As a workaround one can
flush()
between theremove()
and theadd()
call in the example to prevent SQL reordering. But that is of course bad for performance.