I have noticed an oddity in the behavior of Hibernate 6.6.1 that seems to be a bug. Let me explain the problem in the behavior:
There are two entity classes, Class1
and Class2
, Class1
is related to Class2
by a @OneToMany
relation with orphanRemoval = true
, and Class2
respectively has @ManyToOne
relation to Class1
.
@Entity
public class Class1 {
*Another fields*
@OneToMany(mappedBy = "class1", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY)
private List<Class2> class2;
}
@Entity
public class Class2 {
*Another fields*
@ManyToOne(optional = false, fetch = FetchType.LAZY)
@JoinColumn(name = "SOME", referencedColumnName = "SOME", nullable = true)
private Class1 class1;
}
In case Class1 is first created as a proxy and then some element of the Class2
collection is get from it, the class1 field will be populated with a proxy(Class1) instead of an object. Yes, that proxy will be initialized, but the important thing is that the object itself in Class2’s class1
field will be of type proxy.
Now you just do a flush
and you get Don’t change the reference to a collection with delete-orphan enabled
Now let me explain the reason: during the flush the currentKey
will be set for CollectionEntry
in the line: org/hibernate/engine/internal/Collections.java:165
ce.setCurrentKey( type.getKeyOfOwner( entity, session ) );
And it is the proxy object Class1
that will be set there
Further org/hibernate/engine/internal/Collections.java:254
a check will be made.
!currentPersister.getKeyType().isEqual( entry.getLoadedKey(), entry.getCurrentKey(), factory )
Which will suddenly say that currentKey
and loadedKey
are not equal. And they are not equal because for currentKey
the hibernate will try to get the fields directly from proxy in org/hibernate/metamodel/mapping/internal/AbstractEmbeddableMapping.java:113
, which will result in getting null. For loadedKey
, the fields will not equal null because it is a real object.
It seems to me that the hibernate behavior here is completely wrong. I didn’t change the reference to the Class1
object in any way, moreover, if I make a line change to get the object in currentKey
instead of its proxy, the result of isEqual
will be true instead of false
!currentPersister.getKeyType().isEqual( entry.getLoadedKey(), Hibernate.unproxy(entry.getCurrentKey())), factory )
I think hibernate should either do unproxy
beforehand when it saves the currentKey
to CollectionEntry
, or do unproxy
when comparing in org/hibernate/engine/internal/Collections.java:254