Bug with proxy assignment in CollectionEntry.currentKey

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

@Ralin thank you for reporting this and the deep insights into the problem. I agree that Hibernate should handle this correctly, and handle proxies when comparing keys (as long as they’re initialized, at least).

I suggest you open a new Jira issue in our tracker and attach a simple test case that demonstrates the issue or, even better, since you already went through the trouble of finding where the problem resides try fixing it yourself and open a PR against the Hibernate repository.

1 Like