Hibernate-reactive: L'exemple échoue avec ORM 5.4.24

Créé le 20 nov. 2020  ·  37Commentaires  ·  Source: hibernate/hibernate-reactive

Exemple d'un des logs utilisant 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

Tous les 37 commentaires

Goops, vous avez raison : @Sanne et moi avons tous les deux raté cela à cause d'autres échecs fallacieux dans le CI.

Je risque de supposer que cela est lié à #443.

Ce problème est-il lié à DirtyChecking ?
Je pense que ce problème est lié à ceci : https://github.com/hibernate/hibernate-reactive/issues/447

Quoi qu'il en soit, cela arrive après la mise à jour vers ORM 5.4.24

Ce problème est-il lié à DirtyChecking ?

Eh bien, j'ai pensé que le bogue était lié au chargement par lots. Je me trompe peut-être cependant.

Ce problème est-il lié à DirtyChecking ?

Eh bien, j'ai pensé que le bogue était lié au chargement par lots. Je me trompe peut-être cependant.

Bon, on dirait que je me trompe et le problème est bel et bien ailleurs.

Je travaille sur un cas de test. J'espère pouvoir le confirmer assez tôt.

Cela a du sens car seul l'exemple échoue et c'est le seul endroit où nous activons les améliorations du bytecode

D'accord, super.

Donc oui. Le problème est la vérification sale de la collection lorsque les améliorations du byte code sont activées. Je ne sais pas pourquoi ça fonctionnait avant. Probablement, ce bogue dans ORM cachait le problème et maintenant qu'il est corrigé, nous le voyons.

Uff, quel PITA.

OTOH, pourquoi la collection vérifie-t-elle même la _matière_ alors que nous n'avons même pas encore de CollectionPersister ?

C'est le problème :

La méthode améliorée $$_hibernate_hasDirtyAttributes a changé, de

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

à

    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;
    }

l'appel à this.movies.size() provoque un LazyInitializationException

movies est le nom de l'association à l'intérieur du test que j'ai créé

@DavideD alors pensez-vous que ce qui se passe ici est simplement que nous n'avons pas d'initialisation paresseuse transparente (c'est-à-dire qu'il manque un appel à fetch() ), ou est-ce quelque chose de plus profond?

@DavideD J'ai commenté https://github.com/hibernate/hibernate-orm/pull/3645 , car cet appel à size() ne me semble pas correct. (Mais je pourrais facilement manquer quelque chose.)

@DavideD alors pensez-vous que ce qui se passe ici est simplement que nous n'avons pas d'initialisation paresseuse transparente (c'est-à-dire qu'il manque un appel à fetch()), ou est-ce quelque chose de plus profond?

Je pense que c'est le problème. J'essaie de voir si je peux récupérer la collection quelque part afin qu'elle soit déjà récupérée.
Au fait, tout fonctionne si je configure l'association pour la récupération EAGER.

J'essaie de voir si je peux récupérer la collection quelque part afin qu'elle soit déjà récupérée.

OK, mais ne vous tuez pas là-dessus _tout de suite_ parce qu'il me semble toujours mal que nous récupérons une association non récupérée avec mappedBy juste pour vérifier ses éléments. Voyons ce que @beikov et @Sanne ont à dire.

(OTOH, même si le noyau ne fait pas la bonne chose _dans ce cas particulier_, nous pourrions toujours avoir besoin du correctif sur lequel vous travaillez dans d'autres cas.)

Merci @gavinking

Pourquoi la collection de films est-elle != null ? J'ai essayé de reproduire cela dans le noyau, mais je n'ai pas pu car une telle collection ne devrait jamais être non nulle si paresseuse.

Eh bien, je me souviens qu'il y a eu une assez longue discussion quelque part. Un problème que j'ai ouvert où j'ai soutenu que cette "double paresse" ou ces collections étaient au moins sans doute mauvaises dans Hibernate ORM, et un problème bien pire pour les RH, où la récupération n'est pas transparente. Nous devons vous permettre d'obtenir une référence à la collection pour appeler fetch() dessus.

OK trouvé. Cela pourrait-il être lié à #374 ?

Est-ce que collectionsInDefaultFetchGroupEnabled toujours vrai dans RX ?

Hmm, si je le mets sur true, la collection est initialisée avec impatience.

Est-ce que collectionsInDefaultFetchGroupEnabled toujours vrai dans RX ?

Ouais, ça l'est.

Hmm, si je le mets sur true, la collection est initialisée avec impatience.

Hummm... vraiment ? Ce n'était pas l'intention !

collectionsInDefaultFetchGroupEnabled est censé simplement inclure le proxy de collection dans le groupe d'extraction par défaut. Il n'est pas censé récupérer la collection elle-même.

Donc soit j'ai complètement merdé, soit quelque chose a changé depuis que j'ai écrit ce code.

Je suis avec vous que l'approche actuelle de l'amélioration n'est pas idéale et que le champ doit être initialisé sur une collection persistante, mais je pense que le problème que vous rencontrez dans RX pourrait être causé par autre chose. Lorsque je mets l'indicateur sur true, je vois la collection en cours d'initialisation via cette 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)

Donc $$_hibernate_removeDirtyFields() appelle size() sur une collection non initialisée. _Devrait-ce être?

Je suis avec vous que l'approche actuelle de l'amélioration n'est pas idéale et que le champ doit être initialisé sur une collection persistante, mais je pense que le problème que vous rencontrez dans RX pourrait être causé par autre chose. Lorsque je mets l'indicateur sur true, je vois la collection en cours d'initialisation via cette trace :

Il me manque probablement quelque chose, mais n'est-ce pas le même problème ?
Il appelle toujours size() mais à partir d'une méthode différente

Vous avez raison. Apparemment, les collections ne sont pas enregistrées comme étant paresseuses, ce qui provoque ce problème. J'enquête maintenant.

Voici un PR qui devrait résoudre ces problèmes : https://github.com/hibernate/hibernate-orm/pull/3664

Merci @beikov , le test semble fonctionner en utilisant cette branche

@beikov excellent, merci

Merci @beikov , le test semble fonctionner en utilisant cette branche

@DavideD pouvez-vous retester maintenant, @beikov vient de

Voici un PR qui devrait résoudre ces problèmes : hibernate/hibernate-orm#3664

Confirmé, nous avons juste besoin d'une version de hibernate-core .

Corrigé par #463

Merci a tous

Cette page vous a été utile?
0 / 5 - 0 notes