عند محاولة إحضار ارتباط كسول مع Mutiny in Quarkus ، فإنه لا يعمل إذا تم إجراء الجلب في جلسة مختلفة عن تلك المستخدمة لاستمرار البيانات. إذا تم استخدام نفس الجلسة (حتى الجلب مباشرة بعد الاستمرار في نفس الجلسة) ، فإنها تعمل.
سلوك متوقع
يعمل أيضًا في جلسات مختلفة.
السلوك الفعلي
تم طرح استثناء عند محاولة جلب الاقتران:
2021-03-12 09:56:23,634 ERROR [org.jbo.res.rea.com.cor.AbstractResteasyReactiveContext] (vert.x-eventloop-thread-2) Request failed: org.hibernate.LazyInitializationException: Collection cannot be initialized: com.example.Author.books
at org.hibernate.reactive.session.impl.ReactiveSessionImpl.initializeCollection(ReactiveSessionImpl.java:330)
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)
at org.hibernate.collection.internal.AbstractPersistentCollection$1.doWork(AbstractPersistentCollection.java:178)
at org.hibernate.collection.internal.AbstractPersistentCollection$1.doWork(AbstractPersistentCollection.java:163)
at org.hibernate.collection.internal.AbstractPersistentCollection.withTemporarySessionIfNeeded(AbstractPersistentCollection.java:264)
at org.hibernate.collection.internal.AbstractPersistentCollection.readSize(AbstractPersistentCollection.java:162)
at org.hibernate.collection.internal.PersistentBag.size(PersistentBag.java:371)
at org.hibernate.bytecode.enhance.spi.interceptor.LazyAttributeLoadingInterceptor.takeCollectionSizeSnapshot(LazyAttributeLoadingInterceptor.java:160)
at org.hibernate.bytecode.enhance.spi.interceptor.LazyAttributeLoadingInterceptor.lambda$loadAttribute$0(LazyAttributeLoadingInterceptor.java:110)
at org.hibernate.bytecode.enhance.spi.interceptor.EnhancementHelper.performWork(EnhancementHelper.java:130)
at org.hibernate.bytecode.enhance.spi.interceptor.LazyAttributeLoadingInterceptor.loadAttribute(LazyAttributeLoadingInterceptor.java:76)
at org.hibernate.bytecode.enhance.spi.interceptor.LazyAttributeLoadingInterceptor.fetchAttribute(LazyAttributeLoadingInterceptor.java:72)
at org.hibernate.bytecode.enhance.spi.interceptor.LazyAttributeLoadingInterceptor.handleRead(LazyAttributeLoadingInterceptor.java:53)
at org.hibernate.bytecode.enhance.spi.interceptor.AbstractInterceptor.readObject(AbstractInterceptor.java:153)
at com.example.Author.$$_hibernate_read_books(Author.java)
at com.example.Author.getBooks(Author.java:43)
at com.example.TestResource.lambda$getBooks$2(TestResource.java:61)
at io.smallrye.mutiny.operators.UniOnItemTransformToUni.invokeAndSubstitute(UniOnItemTransformToUni.java:31)
at io.smallrye.mutiny.operators.UniOnItemTransformToUni$2.onItem(UniOnItemTransformToUni.java:74)
at io.smallrye.mutiny.context.ContextPropagationUniInterceptor$1.lambda$onItem$1(ContextPropagationUniInterceptor.java:31)
at io.smallrye.context.impl.wrappers.SlowContextualExecutor.execute(SlowContextualExecutor.java:19)
at io.smallrye.mutiny.context.ContextPropagationUniInterceptor$1.onItem(ContextPropagationUniInterceptor.java:31)
at io.smallrye.mutiny.operators.UniSerializedSubscriber.onItem(UniSerializedSubscriber.java:85)
at io.smallrye.mutiny.context.ContextPropagationUniInterceptor$1.lambda$onItem$1(ContextPropagationUniInterceptor.java:31)
at io.smallrye.context.impl.wrappers.SlowContextualExecutor.execute(SlowContextualExecutor.java:19)
at io.smallrye.mutiny.context.ContextPropagationUniInterceptor$1.onItem(ContextPropagationUniInterceptor.java:31)
at io.smallrye.mutiny.operators.UniDelegatingSubscriber.onItem(UniDelegatingSubscriber.java:24)
at io.smallrye.mutiny.context.ContextPropagationUniInterceptor$1.lambda$onItem$1(ContextPropagationUniInterceptor.java:31)
at io.smallrye.context.impl.wrappers.SlowContextualExecutor.execute(SlowContextualExecutor.java:19)
at io.smallrye.mutiny.context.ContextPropagationUniInterceptor$1.onItem(ContextPropagationUniInterceptor.java:31)
at io.smallrye.mutiny.operators.UniSerializedSubscriber.onItem(UniSerializedSubscriber.java:85)
at io.smallrye.mutiny.context.ContextPropagationUniInterceptor$1.lambda$onItem$1(ContextPropagationUniInterceptor.java:31)
at io.smallrye.context.impl.wrappers.SlowContextualExecutor.execute(SlowContextualExecutor.java:19)
at io.smallrye.mutiny.context.ContextPropagationUniInterceptor$1.onItem(ContextPropagationUniInterceptor.java:31)
at io.smallrye.mutiny.operators.uni.builders.UniCreateFromCompletionStage.lambda$forwardFromCompletionStage$1(UniCreateFromCompletionStage.java:30)
at java.base/java.util.concurrent.CompletableFuture.uniWhenComplete(CompletableFuture.java:859)
at java.base/java.util.concurrent.CompletableFuture$UniWhenComplete.tryFire(CompletableFuture.java:837)
at java.base/java.util.concurrent.CompletableFuture.postComplete(CompletableFuture.java:506)
at java.base/java.util.concurrent.CompletableFuture.complete(CompletableFuture.java:2073)
at com.ibm.asyncutil.iteration.AsyncTrampoline$TrampolineInternal.unroll(AsyncTrampoline.java:127)
at com.ibm.asyncutil.iteration.AsyncTrampoline$TrampolineInternal.lambda$unroll$0(AsyncTrampoline.java:123)
at java.base/java.util.concurrent.CompletableFuture.uniWhenComplete(CompletableFuture.java:859)
at java.base/java.util.concurrent.CompletableFuture$UniWhenComplete.tryFire(CompletableFuture.java:837)
at java.base/java.util.concurrent.CompletableFuture.postComplete(CompletableFuture.java:506)
at java.base/java.util.concurrent.CompletableFuture.complete(CompletableFuture.java:2073)
at org.hibernate.reactive.pool.impl.Handlers.lambda$toCompletionStage$0(Handlers.java:26)
at io.vertx.sqlclient.impl.SqlResultHandler.complete(SqlResultHandler.java:98)
at io.vertx.sqlclient.impl.SqlResultHandler.handle(SqlResultHandler.java:87)
at io.vertx.sqlclient.impl.SqlResultHandler.handle(SqlResultHandler.java:33)
at io.vertx.sqlclient.impl.SocketConnectionBase.handleMessage(SocketConnectionBase.java:241)
at io.vertx.sqlclient.impl.SocketConnectionBase.lambda$init$0(SocketConnectionBase.java:88)
at io.vertx.core.net.impl.NetSocketImpl.lambda$new$2(NetSocketImpl.java:101)
at io.vertx.core.streams.impl.InboundBuffer.handleEvent(InboundBuffer.java:237)
at io.vertx.core.streams.impl.InboundBuffer.write(InboundBuffer.java:127)
at io.vertx.core.net.impl.NetSocketImpl.handleMessage(NetSocketImpl.java:357)
at io.vertx.core.impl.ContextImpl.executeTask(ContextImpl.java:366)
at io.vertx.core.impl.EventLoopContext.execute(EventLoopContext.java:43)
at io.vertx.core.impl.ContextImpl.executeFromIO(ContextImpl.java:229)
at io.vertx.core.net.impl.VertxHandler.channelRead(VertxHandler.java:163)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)
at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357)
at io.netty.channel.CombinedChannelDuplexHandler$DelegatingChannelHandlerContext.fireChannelRead(CombinedChannelDuplexHandler.java:436)
at io.vertx.pgclient.impl.codec.PgEncoder.lambda$write$0(PgEncoder.java:78)
at io.vertx.pgclient.impl.codec.PgCommandCodec.handleReadyForQuery(PgCommandCodec.java:138)
at io.vertx.pgclient.impl.codec.PgDecoder.decodeReadyForQuery(PgDecoder.java:226)
at io.vertx.pgclient.impl.codec.PgDecoder.channelRead(PgDecoder.java:86)
at io.netty.channel.CombinedChannelDuplexHandler.channelRead(CombinedChannelDuplexHandler.java:251)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)
at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357)
at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1410)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)
at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:919)
at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:163)
at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:714)
at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:650)
at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:576)
at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:493)
at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:989)
at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
الناسخ
https://github.com/markusdlugi/hibernate-reactive-lazy-fetching
خطوات إعادة إنتاج السلوك:
infrastructure/docker-compose.yml
.mvn quarkus:dev
POST http://localhost:8080/test/working
.GET http://localhost:8080/test/failing/1
(أو مؤلف آخر تم إنشاؤه عبر POST http://localhost:8080/test/failing
).تضمين التغريدة
نعم ، أعتقد أننا قررنا أن هذا هو الشيء الصحيح الذي يجب القيام به: في JPA لا توجد فكرة لإعادة ربط مثيل الكيان بجلسة. هذا يختلف قليلاً عن نمط الاستخدام القديم حقًا لـ Hibernate حيث يمكنك استخدام saveOrUpdate()
لإعادة ربط كائن منفصل مباشرةً. في JPA ، نموذج التعامل مع العناصر المنفصلة هو العملية merge()
.
(وهناك أسباب وجيهة لعدم وجود هذا. التجربة هي أن المستخدمين عمليًا يوقعون أنفسهم في حالة من الفوضى مع إعادة الاقتران.)
نظرًا لأن تحميل مجموعة منفصلة يعني حقًا إعادة ربط أصلها ، فنحن ببساطة لا ندعم ذلك.
ومع ذلك، IIRC، أنا لا تتيح لك القيام بذلك في StatelessSession
، لأنه أكثر توافقا كبيرا مع هذا النموذج البرمجة. حاول استخدام جلسة عديمة الجنسية وأخبرني إذا كانت تناسبك.
مرحبًا gavinking ، شكرًا على الرد السريع.
لست متأكدًا تمامًا مما إذا كنت أتلقى ما تقوله. لذا فإن ما قمت بتطبيقه هو بالضبط ما تم وصفه في الوثائق :
session.find(Author.class, authorId)
.chain(author -> Mutiny.fetch(author.getBooks()));
يعمل هذا إذا استمر المؤلف وكتبه في نفس الجلسة (على سبيل المثال ، في نفس طلب HTTP) ، لكنه لا يعمل إذا استخدمت جلسة أخرى (أي طلب آخر).
إذن أنت تلمح إلى أن هذا السلوك مقصود ، على الأقل لجلسة "عادية" (ذات حالة؟)؟ يبدو لي هذا كحالة استخدام قياسية حقًا ، لأنني سأحاول دائمًا الحصول على بعض البيانات من قاعدة البيانات بعض الوقت بعد الاستمرار في ذلك ، لذلك في جلسة مختلفة. هذا يعني أنه باستخدام جلسة عادية ، لا يتم دعم الارتباطات البطيئة. في هذه الحالة ، أود أن أزعم أنه يجب الإشارة صراحةً في الوثائق إلى ضرورة استخدام StatelessSession
إذا كان سيتم جلب الارتباطات البطيئة.
أقول أنه في الجلسة الجديدة يجب عليك استخدام getReference()
للحصول على مرجع مرتبط بالجلسة للكيان الذي يمتلك المجموعة. ثم يمكنك إحضار المجموعة كيفما تشاء.
هذا هو نموذج JPA القياسي ، بل إنه أسهل قليلاً في استخدامه في الموارد البشرية لأنني أثقلت getReference()
.
gavinking هل تقصد شيئًا كهذا؟
Author reference = session.getReference( Author.class, authorId );
return Mutiny.fetch( reference.getBooks() );
بالتأكيد ، لكني جعلت الأمر أسهل. يمكنك تمرير المؤلف المنفصل مباشرة إلى getReference()
.
حسنًا ، لقد جربت هذه الاقتراحات ، لكن للأسف لم تنجح أيضًا. ربما يمكنك أن تكون لطيفًا جدًا للإشارة إلى ما إذا كنت أفعل شيئًا غير صحيح هنا:
فشل الأسلوب باستخدام getReference()
مع الاستثناء التالي:
2021-03-12 14:17:51,381 ERROR [org.jbo.res.rea.ser.cor.ExceptionMapping] (vert.x-eventloop-thread-10) Request failed : java.lang.NullPointerException
at org.hibernate.engine.internal.AbstractEntityEntry.overwriteLoadedStateCollectionValue(AbstractEntityEntry.java:336)
at org.hibernate.persister.entity.AbstractEntityPersister.initializeLazyProperty(AbstractEntityPersister.java:1144)
at org.hibernate.persister.entity.AbstractEntityPersister.initializeEnhancedEntityUsedAsProxy(AbstractEntityPersister.java:4497)
at org.hibernate.bytecode.enhance.spi.interceptor.EnhancementAsProxyLazinessInterceptor.forceInitialize(EnhancementAsProxyLazinessInterceptor.java:221)
at org.hibernate.bytecode.enhance.spi.interceptor.EnhancementAsProxyLazinessInterceptor.lambda$handleRead$0(EnhancementAsProxyLazinessInterceptor.java:133)
at org.hibernate.bytecode.enhance.spi.interceptor.EnhancementHelper.performWork(EnhancementHelper.java:130)
at org.hibernate.bytecode.enhance.spi.interceptor.EnhancementAsProxyLazinessInterceptor.handleRead(EnhancementAsProxyLazinessInterceptor.java:98)
at org.hibernate.bytecode.enhance.spi.interceptor.AbstractInterceptor.readObject(AbstractInterceptor.java:153)
at com.example.Author.$$_hibernate_read_books(Author.java)
at com.example.Author.getBooks(Author.java:43)
at com.example.TestResource.getBooksUsingReference(TestResource.java:69)
...
الحل الآخر المقترح باستخدام StatelessSession
فشل مع:
2021-03-12 14:21:11,840 ERROR [org.jbo.res.rea.ser.cor.ExceptionMapping] (vert.x-eventloop-thread-5) Request failed : org.hibernate.LazyInitializationException: Unable to perform requested lazy initialization [com.example.Author.books] - no session and settings disallow loading outside the Session
at org.hibernate.bytecode.enhance.spi.interceptor.EnhancementHelper.throwLazyInitializationException(EnhancementHelper.java:199)
at org.hibernate.bytecode.enhance.spi.interceptor.EnhancementHelper.performWork(EnhancementHelper.java:89)
at org.hibernate.bytecode.enhance.spi.interceptor.LazyAttributeLoadingInterceptor.loadAttribute(LazyAttributeLoadingInterceptor.java:76)
at org.hibernate.bytecode.enhance.spi.interceptor.LazyAttributeLoadingInterceptor.fetchAttribute(LazyAttributeLoadingInterceptor.java:72)
at org.hibernate.bytecode.enhance.spi.interceptor.LazyAttributeLoadingInterceptor.handleRead(LazyAttributeLoadingInterceptor.java:53)
at org.hibernate.bytecode.enhance.spi.interceptor.AbstractInterceptor.readObject(AbstractInterceptor.java:153)
at com.example.Author.$$_hibernate_read_books(Author.java)
at com.example.Author.getBooks(Author.java:43)
at com.example.TestResource.lambda$getBooksUsingStatelessSession$3(TestResource.java:78)
...
سألقي نظرة ، يبدو أنه مرتبط بتحسينات الرمز الثانوي
DavideD ، أي تحديث؟
حاولت إعادة إنتاج المشكلة بدون Quarkus باستخدام session-example
في هذا الريبو ، لكنني لم أتمكن من القيام بذلك. فقط مع السبات التفاعلي والتمرد البسيط ، فإنه يعمل كما هو متوقع. وهي تعمل أيضًا باستخدام find()
، ولا يلزم استخدام getReference()
أو StatelessSession
.
لذلك يبدو أن هذه المشكلة تحدث فقط في Quarkus.
ما زلت أبحث في الأمر.
تحدث المشكلة بسبب تحسينات كود البايت التي يتم تمكينها افتراضيًا في Quarkus.
لم أكتشف بعد أي مجموعة من الخصائص تعيد إنشاء المشكلة عند استخدام Hibernate Reactive فقط. لكني سأستمر في العمل عليها اليوم
يمكنني الآن إعادة إنشاء الخطأ في رد الفعل. يتطلب شيئين:
hibernate.bytecode.allow_enhancement_as_proxy
على trueSessionFactoryOptions#enableCollectionInDefaultFetchGroup
على false. يجب أن يتم ضبطه على صحيح: https://github.com/hibernate/hibernate-reactive/blob/a3f4c61eef71baab8ca321da5af0bfb9052bde4b/hibernate-reactive-core/src/main/java/org/hibernate/reactiveSactoryFactoryuctive. # L27gavinking هل أنا محق في التفكير في أننا يجب أن نتأكد من تعيين enableCollectionInDefaultFetchGroup
في Quarkus على false؟ أعترف أنه في الوقت الحالي لا أفهم المشكلة بالكامل (حاولت متابعة المحادثة بشأن المشكلة رقم 374)
ام. أعتقد أنه من المنطقي أن يكون لديك المجموعة في مجموعة الجلب الافتراضية. أنا لا أعتقد أنه من أي وقت مضى من المنطقي أن استبعاده.
n تم تعيين Quarkus enableCollectionInDefaultFetchGroup على false
عذرًا ، أجل ، قصدت كتابة is set to true
. وقت لل استراحة :-)
آه حسنًا ، حسنًا :-) خذ قسطًا من الراحة :-)
DavideD هذه بعض النتائج الرائعة ، شكرًا لك :)
أردت فقط أن أشير إلى أنه وفقًا للمناقشة في # 374 وما يرتبط به من السبات / السبات # 3558 ، يبدو أنه تم ترك enableCollectionInDefaultFetchGroup
عمداً ليكون false
في Quarkus؟ على الأقل هذا ما أجمعه من هذا التعليق:
https://github.com/hibernate/hibernate-orm/pull/3558#issuecomment -695003875
على أي حال ، يبدو أن Quarkus لا يعين هذه الخاصية حاليًا على true
، لأنه غير مكوّن في FastBootReactiveEntityManagerFactoryBuilder
:
وإذا قمت بالتحقق من ذلك بشكل صحيح ، فإن الافتراضي في Hibernate ORM لا يزال يبدو أنه false
، وهو ما يفسر سبب رؤيتنا لهذا السلوك في Quarkus فقط.
Sanne بحاجة إلى إلقاء نظرة على هذا.
إذا اضطررنا فقط إلى ضبطها على الحقيقة ، فيبدو من السهل جدًا حلها. سوف أقوم بإنشاء إصلاح ثم تحقق من Sanne
حسنًا ، ما أتساءل عنه هو ما إذا كان مضبوطًا على "خطأ" لأن هذا هو ما يحتاجه ORM ، وإذا كنا بحاجة إلى توخي الحذر حتى لا نتدخل في وضع السبات العادي؟
اكتشاف رائع ، شكرًا. نعم ، هذا صعب بعض الشيء لأن الامتدادين ("العادي" و "التفاعلي") مرتبطان بدرجة عالية من أجهزة الصراف الآلي.
تحتاج إلى فصلهم بشكل صحيح ، تم تطوير ذلك في اندفاع كبير.
(OTOH ، لست متأكدًا من سبب احتياج ORM إليه ؛ كما جادلت من قبل ، أعتقد أنه يجب تشغيله افتراضيًا.)
همهمة طيب. لنفعل هذا لكل من ORMs (في Quarkus).
gavinking ، أعتقد أن العنوان خاطئ ، أليس كذلك؟ أعتقد أنك قصدت وضع enableCollectionInDefaultFetchGroup
هناك بدلاً من ذلك. تحسينات Bytecode ضرورية لـ Quarkus على أي حال بسبب مواطن ، أفترض :)
آه أجل ، بالتأكيد ، أعتقد أنك على حق.
أنت تقول أنك ترى هذا الخطأ بالإعدادات الافتراضية في Quarkus؟
نعم ، لا يلزم تكوين خاص في Quarkus للحصول على هذا الخطأ. كان على DavideD فقط إجراء بعض التهيئة الخاصة لإعادة الإنتاج في HR العادي (بدون Quarkus).
بالتأكيد.
الشيء هو أنه ليس خطأ في الموارد البشرية. لقد أضفت عمدًا أن "التجميع في مجموعة الجلب الافتراضية" شيء أساسي على وجه التحديد لأننا نحتاج إليه طوال الوقت في الموارد البشرية.
لذا فإن الخطأ موجود في امتداد Quarkus الذي يطفئه.
(أو ، يمكن القول ، في الجوهر لفعل الشيء الخطأ بشكل افتراضي).
لقد أرسلت إصلاحًا لـ Quarkus: https://github.com/quarkusio/quarkus/pull/15818
لا يبدو أن getReference
يعمل على quarkus في الوقت الحالي. سوف أقوم بإنشاء قضية منفصلة لذلك.
يبدو رائعا ، شكرا لك! 👍
اختبرته للتو ويمكن أن تؤكد أن جلب المجموعات البطيئة يعمل الآن في Quarkus. شكرا مرة أخرى: ابتسم:
شكرا لكmarkusdlugi
شكرًا للإبلاغ عن هذا markusdlugi
التعليق الأكثر فائدة
اختبرته للتو ويمكن أن تؤكد أن جلب المجموعات البطيئة يعمل الآن في Quarkus. شكرا مرة أخرى: ابتسم: