Björn Zurmaar (
https://hibernate.atlassian.net/secure/ViewProfile.jspa?accountId=5ecd86c...
) *created* an issue
Hibernate ORM (
https://hibernate.atlassian.net/browse/HHH?atlOrigin=eyJpIjoiMjcwNThmZGE5...
) / Bug (
https://hibernate.atlassian.net/browse/HHH-14043?atlOrigin=eyJpIjoiMjcwNT...
) HHH-14043 (
https://hibernate.atlassian.net/browse/HHH-14043?atlOrigin=eyJpIjoiMjcwNT...
) Two concurrent element shifts in a list damage database integrity. (
https://hibernate.atlassian.net/browse/HHH-14043?atlOrigin=eyJpIjoiMjcwNT...
)
Issue Type: Bug Affects Versions: 5.4.16 Assignee: Unassigned Attachments:
hhh-broken-list.zip Components: hibernate-entitymanager Created: 26/May/2020 15:40 PM
Environment: JDK 11, Hibernate Core 5.4.16 Priority: Major Reporter: Björn Zurmaar (
https://hibernate.atlassian.net/secure/ViewProfile.jspa?accountId=5ecd86c...
)
*Problem description:* Assume you have an entity Foo having a list of Bars. It's a
unidirectional OneToMany mapping with a @JoinColumn annotation. When shifting element in
the lists from within two different transactions starting concurrently the database gets
inconsistent and elements disappear when the range of affected elements overlaps.
*Example:* when you have a list with three elements and shift element 0 to index 1 in one
and element 2 to 1 in another transaction then you'll end up with a table with two
indices 1 and one index 2. Reloading the collection in hibernate in a new transaction the
element at index 0 is then 0. More generally speaking arbitrary list elements may vanish
in this scenario when working with bigger lists and other elements to be shifted. There is
also no way to reinsert the vanished element into the collection unless you have it cached
somewhere else.
*Analysis:* This behavior is caused by the way hibernate rewrites the list's indices.
Only affected indices will be updated. When the shift affects n list elements only the
indices of the n affected elements will be updated. Hibernate generates 2*n UPDATE
statements for this. The first n to set all the affected indices to null, the second n
statements to write the new index to the rows. This is a perfectly fine and clever
optimization in a single transactioned / threaded environment but fails when multiple
transactions are involved. That is because the transaction manager does not know about
lists and applies both changes consecutively. It does not have a chance to detect the
resulting conflict because the changes are incomplete from a transactional point of
view.
*Suggestion:* Hibernate should update all list indices when it detects that elements of a
collection were moved. Regardless of the transaction interleaving this would guarantee
that database remains in a consistent state. The transaction that is closed last would
then define the state without damaging database consistency.
I have a attached a unit test exposing the undesired behavior.
(
https://hibernate.atlassian.net/browse/HHH-14043#add-comment?atlOrigin=ey...
) Add Comment (
https://hibernate.atlassian.net/browse/HHH-14043#add-comment?atlOrigin=ey...
)
Get Jira notifications on your phone! Download the Jira Cloud app for Android (
https://play.google.com/store/apps/details?id=com.atlassian.android.jira....
) or iOS (
https://itunes.apple.com/app/apple-store/id1006972087?pt=696495&ct=Em...
) This message was sent by Atlassian Jira (v1001.0.0-SNAPSHOT#100127- sha1:dccb6b5 )