Hibernate-reactive: Example fails with ORM 5.4.24

Created on 20 Nov 2020  ·  37Comments  ·  Source: hibernate/hibernate-reactive

Example of one of the logs using org.hibernate:hibernate-core:5.5.0-SNAPSHOT:20201117.201048-196:

Feersum Endjinn is a great book!
[ERROR] failed to execute statement [select author0_.id as id1_0_0_, author0_.name as name2_0_0_ from authors author0_ where author0_.id in (?,?)]
[ERROR] could not load an entity batch: [org.hibernate.example.reactive.Author#<1, 3>]
java.util.concurrent.CompletionException: org.hibernate.LazyInitializationException: Collection cannot be initialized: org.hibernate.example.reactive.Author.books
    at java.util.concurrent.CompletableFuture.encodeThrowable(CompletableFuture.java:273) ~[?:1.8.0_275]
    at java.util.concurrent.CompletableFuture.completeThrowable(CompletableFuture.java:280) ~[?:1.8.0_275]
    at java.util.concurrent.CompletableFuture.uniAccept(CompletableFuture.java:673) ~[?:1.8.0_275]
    at java.util.concurrent.CompletableFuture.uniAcceptStage(CompletableFuture.java:683) ~[?:1.8.0_275]
    at java.util.concurrent.CompletableFuture.thenAccept(CompletableFuture.java:2010) ~[?:1.8.0_275]
    at java.util.concurrent.CompletableFuture.thenAccept(CompletableFuture.java:110) ~[?:1.8.0_275]
    at org.hibernate.reactive.loader.ReactiveLoaderBasedResultSetProcessor.reactiveInitializeEntitiesAndCollections(ReactiveLoaderBasedResultSetProcessor.java:150) ~[hibernate-reactive-core-1.0.0-SNAPSHOT.jar:1.0.0-SNAPSHOT]
    at org.hibernate.reactive.loader.ReactiveLoaderBasedResultSetProcessor.reactiveExtractResults(ReactiveLoaderBasedResultSetProcessor.java:83) ~[hibernate-reactive-core-1.0.0-SNAPSHOT.jar:1.0.0-SNAPSHOT]
Error: Exception in thread "main" java.util.concurrent.CompletionException: org.hibernate.LazyInitializationException: Collection cannot be initialized: org.hibernate.example.reactive.Author.books
    at java.util.concurrent.CompletableFuture.encodeThrowable(CompletableFuture.java:273)
    at java.util.concurrent.CompletableFuture.completeThrowable(CompletableFuture.java:280)
    at org.hibernate.reactive.loader.ReactiveLoader.reactiveProcessResultSet(ReactiveLoader.java:123) ~[hibernate-reactive-core-1.0.0-SNAPSHOT.jar:1.0.0-SNAPSHOT]
    at org.hibernate.reactive.loader.ReactiveLoader.lambda$doReactiveQueryAndInitializeNonLazyCollections$0(ReactiveLoader.java:71) ~[hibernate-reactive-core-1.0.0-SNAPSHOT.jar:1.0.0-SNAPSHOT]
    at java.util.concurrent.CompletableFuture.uniCompose(CompletableFuture.java:966) ~[?:1.8.0_275]
    at java.util.concurrent.CompletableFuture$UniCompose.tryFire(CompletableFuture.java:940) ~[?:1.8.0_275]
    at java.util.concurrent.CompletableFuture.postComplete(CompletableFuture.java:488) ~[?:1.8.0_275]
    at java.util.concurrent.CompletableFuture.complete(CompletableFuture.java:1975) ~[?:1.8.0_275]

...

Caused by: org.hibernate.LazyInitializationException: Collection cannot be initialized: org.hibernate.example.reactive.Author.books
    at org.hibernate.reactive.session.impl.ReactiveSessionImpl.initializeCollection(ReactiveSessionImpl.java:320)
    at org.hibernate.collection.internal.AbstractPersistentCollection$4.doWork(AbstractPersistentCollection.java:589)
    at org.hibernate.collection.internal.AbstractPersistentCollection.withTemporarySessionIfNeeded(AbstractPersistentCollection.java:264)
    at org.hibernate.collection.internal.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:585)
    at org.hibernate.collection.internal.AbstractPersistentCollection.read(AbstractPersistentCollection.java:149)
bug

All 37 comments

Goops, you're right: @Sanne and I both missed this because of other spurious failures in the CI.

I hazard a guess that this is related to #443.

Is that issue related to DirtyChecking?
I think that problem is related to this: https://github.com/hibernate/hibernate-reactive/issues/447

Anyway, it happens after the update to ORM 5.4.24

Is that issue related to DirtyChecking?

Well, I figured that the bug is related to batch loading. I might be wrong though.

Is that issue related to DirtyChecking?

Well, I figured that the bug is related to batch loading. I might be wrong though.

Oh well, it looks like I'm wrong and the problem is indeed elsewhere.

I'm working on a test case. Hopefully I will be able to confirm this soon enough.

It makes sense though because only the example fails and it is the only place where we enable the bytecode enhancements

OK, great.

So, yes. The problem is the dirty checking of the collection when byte code enhancements are enabled. I don't know why it was working before. Probably, this bug in ORM was hiding the problem and now that it's fixed we see it.

Uff, what a PITA.

OTOH, why does collection dirty checking even _matter_ when we don't even have CollectionPersisters yet?

This is the issue:

The enhanced method $$_hibernate_hasDirtyAttributes has changed, from

    public boolean $$_hibernate_hasDirtyAttributes() {
        boolean var1 = false;
        var1 = this.$$_hibernate_tracker != null && !this.$$_hibernate_tracker.isEmpty();
        return var1;
    }

to

    public boolean $$_hibernate_hasDirtyAttributes() {
        boolean var1 = false;
        var1 = this.$$_hibernate_tracker != null && !this.$$_hibernate_tracker.isEmpty() || this.$$_hibernate_areCollectionFieldsDirty();
        return var1;
    }

    public boolean $$_hibernate_areCollectionFieldsDirty() {
        boolean var1 = false;
        if (!var1 && this.$$_hibernate_collectionTracker != null) {
            if (this.movies == null && this.$$_hibernate_collectionTracker.getSize("movies") != -1) {
                var1 = true;
            } else if (this.movies != null && this.$$_hibernate_collectionTracker.getSize("movies") != this.movies.size()) {
                var1 = true;
            }
        }

        return var1;
    }

the call to this.movies.size() causes a LazyInitializationException

movies is the name of the association inside the test I've created

@DavideD so do you think that what is going on here is just that we don't have transparent lazy init (i.e. it is missing a call to fetch()), or is it something deeper?

@DavideD I've commented on https://github.com/hibernate/hibernate-orm/pull/3645, because that call to size() doesn't look right to me. (But I could easily be missing something.)

@DavideD so do you think that what is going on here is just that we don't have transparent lazy init (i.e. it is missing a call to fetch()), or is it something deeper?

I think that's the issue. I'm trying to see if I can fetch the collection somewhere so that it is already fetched.
By the way, everything works if I set the association for EAGER fetching.

I'm trying to see if I can fetch the collection somewhere so that it is already fetched.

OK, but don't kill yourself on that _right now_ because it still feels wrong to me that we're fetching an un fetched association with mappedBy just to dirty check its elements. Let's see what @beikov and @Sanne have to say.

(OTOH, even if core isn't doing the right thing _in this particular case_, we still might need the fix your working on in other cases.)

Thanks @gavinking

Why is the movies collection != null? I tried to reproduce this in core, but couldn't because such a collection should never be non-null if lazy.

Well I recall that there was a kinda long discussion somewhere. An issue I opened where I argued that this "double-laziness" or collections was at least arguably bad in Hibernate ORM, and a much worse problem for HR, where fetching isn't transparent. We need to let you obtain a reference to the collection to call fetch() on it.

OK found it. So could this be related to #374?

Is collectionsInDefaultFetchGroupEnabled always true in RX?

Hmm, if I set it to true, the collection is eagerly initialized.

Is collectionsInDefaultFetchGroupEnabled always true in RX?

Yup, it is.

Hmm, if I set it to true, the collection is eagerly initialized.

Ummmm ... really? That wasn't the intention!

collectionsInDefaultFetchGroupEnabled is supposed to just include the collection proxy in the default fetch group. It's not supposed to actually fetch the collection itself.

So either I totally screwed up, or something has changed since I wrote that code.

I'm with you that the current approach to enhancement is not ideal and that the field should be initialized to a persistent collection, but I think that the issue you are having in RX might be caused by something else. When I set the flag to true, I see the collection being initialized through this trace:

org.hibernate.collection.internal.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:585)
org.hibernate.collection.internal.AbstractPersistentCollection.read(AbstractPersistentCollection.java:149)
org.hibernate.collection.internal.AbstractPersistentCollection$1.doWork(AbstractPersistentCollection.java:178)
org.hibernate.collection.internal.AbstractPersistentCollection$1.doWork(AbstractPersistentCollection.java:163)
org.hibernate.collection.internal.AbstractPersistentCollection.withTemporarySessionIfNeeded(AbstractPersistentCollection.java:264)
org.hibernate.collection.internal.AbstractPersistentCollection.readSize(AbstractPersistentCollection.java:162)
org.hibernate.collection.internal.PersistentBag.size(PersistentBag.java:371)
org.hibernate.test.bytecode.enhancement.dirty.DirtyTrackingCollectionTest$StringsEntity.$$_hibernate_removeDirtyFields(DirtyTrackingCollectionTest.java)
org.hibernate.test.bytecode.enhancement.dirty.DirtyTrackingCollectionTest$StringsEntity.$$_hibernate_clearDirtyCollectionNames(DirtyTrackingCollectionTest.java)
org.hibernate.test.bytecode.enhancement.dirty.DirtyTrackingCollectionTest$StringsEntity.$$_hibernate_clearDirtyAttributes(DirtyTrackingCollectionTest.java)
org.hibernate.tuple.entity.PojoEntityTuplizer.afterInitialize(PojoEntityTuplizer.java:228)
org.hibernate.persister.entity.AbstractEntityPersister.afterInitialize(AbstractEntityPersister.java:5093)
org.hibernate.engine.internal.TwoPhaseLoad.afterInitialize(TwoPhaseLoad.java:405)
org.hibernate.loader.plan.exec.process.internal.AbstractRowReader.afterInitialize(AbstractRowReader.java:271)
org.hibernate.loader.plan.exec.process.internal.AbstractRowReader.finishUp(AbstractRowReader.java:214)
org.hibernate.loader.plan.exec.process.internal.ResultSetProcessorImpl.extractResults(ResultSetProcessorImpl.java:96)
org.hibernate.loader.plan.exec.internal.AbstractLoadPlanBasedLoader.executeLoad(AbstractLoadPlanBasedLoader.java:105)
org.hibernate.loader.entity.plan.AbstractLoadPlanBasedEntityLoader.load(AbstractLoadPlanBasedEntityLoader.java:285)
org.hibernate.persister.entity.AbstractEntityPersister.doLoad(AbstractEntityPersister.java:4441)
org.hibernate.persister.entity.AbstractEntityPersister.load(AbstractEntityPersister.java:4431)
org.hibernate.event.internal.DefaultLoadEventListener.loadFromDatasource(DefaultLoadEventListener.java:569)
org.hibernate.event.internal.DefaultLoadEventListener.doLoad(DefaultLoadEventListener.java:537)
org.hibernate.event.internal.DefaultLoadEventListener.load(DefaultLoadEventListener.java:208)
org.hibernate.event.internal.DefaultLoadEventListener.proxyOrLoad(DefaultLoadEventListener.java:332)
org.hibernate.event.internal.DefaultLoadEventListener.doOnLoad(DefaultLoadEventListener.java:108)
org.hibernate.event.internal.DefaultLoadEventListener.onLoad(DefaultLoadEventListener.java:74)
org.hibernate.event.service.internal.EventListenerGroupImpl.fireEventOnEachListener(EventListenerGroupImpl.java:121)
org.hibernate.internal.SessionImpl.fireLoadNoChecks(SessionImpl.java:1186)
org.hibernate.internal.SessionImpl.fireLoad(SessionImpl.java:1175)
org.hibernate.internal.SessionImpl.access$2100(SessionImpl.java:193)
org.hibernate.internal.SessionImpl$IdentifierLoadAccessImpl.doLoad(SessionImpl.java:2786)
org.hibernate.internal.SessionImpl$IdentifierLoadAccessImpl.lambda$load$1(SessionImpl.java:2767)
org.hibernate.internal.SessionImpl$IdentifierLoadAccessImpl.perform(SessionImpl.java:2723)
org.hibernate.internal.SessionImpl$IdentifierLoadAccessImpl.load(SessionImpl.java:2767)
org.hibernate.internal.SessionImpl.find(SessionImpl.java:3322)
org.hibernate.internal.SessionImpl.find(SessionImpl.java:3284)
org.hibernate.test.bytecode.enhancement.dirty.DirtyTrackingCollectionTest.lambda$test$1(DirtyTrackingCollectionTest.java:57)
org.hibernate.testing.transaction.TransactionUtil.doInJPA(TransactionUtil.java:235)
org.hibernate.testing.transaction.TransactionUtil.doInJPA(TransactionUtil.java:276)
org.hibernate.test.bytecode.enhancement.dirty.DirtyTrackingCollectionTest.test(DirtyTrackingCollectionTest.java:56)
sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
java.lang.reflect.Method.invoke(Method.java:498)
org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
org.hibernate.testing.junit4.ExtendedFrameworkMethod.invokeExplosively(ExtendedFrameworkMethod.java:45)
org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:27)
org.junit.internal.runners.statements.FailOnTimeout$CallableStatement.call(FailOnTimeout.java:298)
org.junit.internal.runners.statements.FailOnTimeout$CallableStatement.call(FailOnTimeout.java:292)
java.util.concurrent.FutureTask.run(FutureTask.java:266)
java.lang.Thread.run(Thread.java:748)

So $$_hibernate_removeDirtyFields() is calling size() on an uninitialized collection. _Should_ it be?

I'm with you that the current approach to enhancement is not ideal and that the field should be initialized to a persistent collection, but I think that the issue you are having in RX might be caused by something else. When I set the flag to true, I see the collection being initialized through this trace:

I'm probably missing something, but isn't this the same issue?
It's still calling size() but from a different method

You are right. Apparently the collections aren't registered as being lazy which causes this problem. I'm investigating now.

Here a PR that should fix these issues: https://github.com/hibernate/hibernate-orm/pull/3664

Thanks @beikov, the test seems to work using that branch

@beikov excellent, thanks

Thanks @beikov, the test seems to work using that branch

@DavideD can you retest now, @beikov just updated the PR, let's see if it still fixes the problem.

Here a PR that should fix these issues: hibernate/hibernate-orm#3664

Confirmed, we just need a release of hibernate-core.

Fixed by #463

Thanks all

Was this page helpful?
0 / 5 - 0 ratings

Related issues

Xset-s picture Xset-s  ·  3Comments

gavinking picture gavinking  ·  6Comments

aguibert picture aguibert  ·  28Comments

gavinking picture gavinking  ·  23Comments

yaakov-berkovitch picture yaakov-berkovitch  ·  7Comments