Hibernate-reactive: Quarkus์—์„œ ๊ฐ€์ ธ์˜ค๋Š” ๊ฒŒ์œผ๋ฅธ ์ปฌ๋ ‰์…˜

์— ๋งŒ๋“  2021๋…„ 03์›” 12์ผ  ยท  30์ฝ”๋ฉ˜ํŠธ  ยท  ์ถœ์ฒ˜: hibernate/hibernate-reactive

Quarkus์—์„œ Mutiny์™€์˜ ์—ฐ๊ฒฐ์„ ๋Š๋ฆฌ๊ฒŒ ๊ฐ€์ ธ์˜ค๋ ค๊ณ  ํ•  ๋•Œ ๋ฐ์ดํ„ฐ๋ฅผ ์œ ์ง€ํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋œ ์„ธ์…˜๊ณผ ๋‹ค๋ฅธ ์„ธ์…˜์—์„œ ๊ฐ€์ ธ์˜ค๊ธฐ๊ฐ€ ์ˆ˜ํ–‰๋˜๋ฉด ์ž‘๋™ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋™์ผํ•œ ์„ธ์…˜์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ(๋”ฐ๋ผ์„œ ๋™์ผํ•œ ์„ธ์…˜์—์„œ ์œ ์ง€ํ•œ ์งํ›„ ๊ฐ€์ ธ์˜ค๊ธฐ) ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค.

์˜ˆ์ƒ๋˜๋Š” ํ–‰๋™
๋‹ค๋ฅธ ์„ธ์…˜์—์„œ๋„ ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค.

์‹ค์ œ ํ–‰๋™
์—ฐ๊ฒฐ์„ ๊ฐ€์ ธ์˜ค๋ ค๊ณ  ํ•  ๋•Œ ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.

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

๋™์ž‘์„ ์žฌํ˜„ํ•˜๋Š” ๋‹จ๊ณ„:

  1. infrastructure/docker-compose.yml ์‚ฌ์šฉํ•˜์—ฌ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ฅผ ์‹œ์ž‘ํ•ฉ๋‹ˆ๋‹ค.
  2. mvn quarkus:dev ์‚ฌ์šฉํ•˜์—ฌ Quarkus ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์‹œ์ž‘
  3. ์š”์ฒญ ๋ณด๋‚ด๊ธฐ: POST http://localhost:8080/test/working .
    3.1. ์ด๊ฒƒ์€ ์ž‘๋™ํ•˜๊ณ  ์ฑ… ๋ชฉ๋ก์„ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
  4. ์š”์ฒญ ๋ณด๋‚ด๊ธฐ: GET http://localhost:8080/test/failing/1 (๋˜๋Š” POST http://localhost:8080/test/failing ๋ฅผ ํ†ตํ•ด ์ƒ์„ฑ๋œ ๋‹ค๋ฅธ ์ž‘์„ฑ์ž ID).
    4.1 ์œ„์˜ ์˜ˆ์™ธ๋ฅผ ์ œ์™ธํ•˜๊ณ ๋Š” ์‹คํŒจํ•ฉ๋‹ˆ๋‹ค.
  • Quarkus ๋ฒ„์ „: 1.12.2
  • Hibernate Reactive Version: 1.0.0.Beta4(Maven์—์„œ ๋ฒ„์ „์„ ๋ฎ์–ด์จ์„œ 1.0.0.CR1์—์„œ๋„ ํ…Œ์ŠคํŠธ๋จ, ๋™์ผํ•œ ๋™์ž‘)
  • ์ตœ๋Œ€ ์ ˆ์ „ ๋ชจ๋“œ ์ฝ”์–ด ๋ฒ„์ „: 5.4.28.Final(๋™์ผํ•œ ๋™์ž‘์ธ Maven์˜ ๋ฒ„์ „์„ ๋ฎ์–ด์จ์„œ 5.4.29.Final์—์„œ๋„ ํ…Œ์ŠคํŠธ๋จ)

๊ฐ€์žฅ ์œ ์šฉํ•œ ๋Œ“๊ธ€

๋ฐฉ๊ธˆ ํ…Œ์ŠคํŠธํ–ˆ๊ณ  ์ด์ œ Quarkus์—์„œ ์ง€์—ฐ ์ปฌ๋ ‰์…˜ ๊ฐ€์ ธ์˜ค๊ธฐ๊ฐ€ ์ž‘๋™ํ•˜๋Š”์ง€ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋‹ค์‹œ ํ•œ ๋ฒˆ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค :์Šค๋งˆ์ผ:

๋ชจ๋“  30 ๋Œ“๊ธ€

/cc @DavidD @gavinking

์˜ˆ, ์šฐ๋ฆฌ๋Š” ์ด๊ฒƒ์ด ์˜ฌ๋ฐ”๋ฅธ ์ผ์ด๋ผ๊ณ  ๊ฒฐ์ •ํ–ˆ๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. JPA์—๋Š” ์—”ํ„ฐํ‹ฐ ์ธ์Šคํ„ด์Šค๋ฅผ ์„ธ์…˜๊ณผ ๋‹ค์‹œ ์—ฐ๊ฒฐํ•œ๋‹ค๋Š” ๊ฐœ๋…์ด ์—†์Šต๋‹ˆ๋‹ค. ๊ทธ๊ฒƒ์€ saveOrUpdate() ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ถ„๋ฆฌ๋œ ๊ฐ์ฒด๋ฅผ ์ง์ ‘ ์žฌ์—ฐ๊ฒฐํ•  ์ˆ˜ ์žˆ๋Š” Hibernate์˜ ์ •๋ง ์˜ค๋ž˜๋œ ์‚ฌ์šฉ ํŒจํ„ด๊ณผ ์•ฝ๊ฐ„ ๋‹ค๋ฆ…๋‹ˆ๋‹ค. JPA์—์„œ ๋ถ„๋ฆฌ๋œ ๊ฐœ์ฒด๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” ๋ชจ๋ธ์€ merge() ์ž‘์—…์ž…๋‹ˆ๋‹ค.

(๊ทธ๋ฆฌ๊ณ  ์ด๊ฒƒ์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š” ๋ฐ๋Š” ์ข‹์€ ์ด์œ ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ฒฝํ—˜์ƒ ์‹ค์ œ๋กœ ์‚ฌ์šฉ์ž๋Š” ์žฌ๊ฒฐํ•ฉ์œผ๋กœ ์ธํ•ด ํ˜ผ๋ž€์— ๋น ์ง€๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.)

๋ถ„๋ฆฌ๋œ ์ปฌ๋ ‰์…˜์„ ๋กœ๋“œํ•˜๋Š” ๊ฒƒ์€ ์‹ค์ œ๋กœ ๋ถ€๋ชจ๋ฅผ ๋‹ค์‹œ ์—ฐ๊ฒฐํ•˜๋Š” ๊ฒƒ์„ ์˜๋ฏธํ•˜๋ฏ€๋กœ ๋‹จ์ˆœํžˆ ์ง€์›ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

๊ทธ๋Ÿฌ๋‚˜ IIRC์—์„œ๋Š” StatelessSession ํ”„๋กœ๊ทธ๋ž˜๋ฐ ๋ชจ๋ธ๊ณผ ํ›จ์”ฌ ๋” ํ˜ธํ™˜๋˜๊ธฐ ๋•Œ๋ฌธ์— ์ด ์ž‘์—…์„ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ƒํƒœ ๋น„์ €์žฅ ์„ธ์…˜์„ ์‚ฌ์šฉํ•ด ๋ณด๊ณ  ํšจ๊ณผ๊ฐ€ ์žˆ๋Š”์ง€ ์•Œ๋ ค์ฃผ์‹ญ์‹œ์˜ค.

@gavinking๋‹˜ , ๋น ๋ฅธ ๋‹ต๋ณ€ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค.

๋ง์”€ํ•˜์‹  ๋‚ด์šฉ์„ ์ดํ•ดํ•˜๊ณ  ์žˆ๋Š”์ง€ ์ž˜ ๋ชจ๋ฅด๊ฒ ์Šต๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ ๋‚ด๊ฐ€ ๊ตฌํ˜„ํ•œ ๊ฒƒ์€ ๋ฌธ์„œ์— ์„ค๋ช…๋œ ๊ฒƒ๊ณผ ์ •ํ™•ํžˆ ๊ฐ™์Šต๋‹ˆ๋‹ค.

session.find(Author.class, authorId)
    .chain(author -> Mutiny.fetch(author.getBooks()));

์ด๊ฒƒ์€ ์ €์ž์™€ ๊ทธ์˜ ์ฑ…์ด ๋™์ผํ•œ ์„ธ์…˜(์ฆ‰, ๋™์ผํ•œ HTTP ์š”์ฒญ์—์„œ)์œผ๋กœ ์ง€์†๋œ ๊ฒฝ์šฐ ์ž‘๋™ํ•˜์ง€๋งŒ ๋‹ค๋ฅธ ์„ธ์…˜(์ฆ‰, ๋‹ค๋ฅธ ์š”์ฒญ)์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ ์ž‘๋™ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

๊ทธ๋ž˜์„œ ์ด๊ฒƒ์ด ์ ์–ด๋„ "์ •์ƒ์ ์ธ"(์ƒํƒœ ์ €์žฅ?) ์„ธ์…˜์— ๋Œ€ํ•ด ์˜๋„๋œ ๋™์ž‘์ด๋ผ๋Š” ๊ฒƒ์„ ์˜๋ฏธํ•ฉ๋‹ˆ๊นŒ? ์ด๊ฒƒ์€ ๋‚ด๊ฐ€ ๊ฑฐ์˜ ํ•ญ์ƒ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ฅผ ์œ ์ง€ํ•œ ํ›„ ์ผ์ • ์‹œ๊ฐ„์ด ์ง€๋‚˜๋ฉด ๋‹ค๋ฅธ ์„ธ์…˜์—์„œ ์ผ๋ถ€ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ค๋ ค๊ณ  ์‹œ๋„ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ด๊ฒƒ์€ ์ •๋ง ํ‘œ์ค€์ ์ธ ์‚ฌ์šฉ ์‚ฌ๋ก€์ฒ˜๋Ÿผ ๋ณด์ž…๋‹ˆ๋‹ค. ์ด๋Š” ์ผ๋ฐ˜ ์„ธ์…˜์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ ์ง€์—ฐ ์—ฐ๊ฒฐ์ด ์ง€์›๋˜์ง€ ์•Š์Œ์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค. ์ด ๊ฒฝ์šฐ ๋ฌธ์„œ์—์„œ ์ง€์—ฐ ์—ฐ๊ฒฐ์„ ๊ฐ€์ ธ์˜ค๋ ค๋ฉด StatelessSession ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค๊ณ  ๋ช…์‹œ์ ์œผ๋กœ ์–ธ๊ธ‰ํ•ด์•ผ ํ•œ๋‹ค๊ณ  ์ฃผ์žฅํ•ฉ๋‹ˆ๋‹ค.

์ƒˆ ์„ธ์…˜์—์„œ getReference() ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ปฌ๋ ‰์…˜์„ ์†Œ์œ ํ•œ ์—”ํ„ฐํ‹ฐ์— ๋Œ€ํ•œ ์„ธ์…˜ ๋ฐ”์ธ๋”ฉ ์ฐธ์กฐ๋ฅผ ๊ฐ€์ ธ์™€์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฐ ๋‹ค์Œ ์›ํ•˜๋Š” ๋Œ€๋กœ ์ปฌ๋ ‰์…˜์„ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ด๊ฒƒ์€ ํ‘œ์ค€ JPA ๋ชจ๋ธ์ด๋ฉฐ getReference() ์˜ค๋ฒ„๋กœ๋“œํ–ˆ๊ธฐ ๋•Œ๋ฌธ์— HR์—์„œ ์‚ฌ์šฉํ•˜๊ธฐ๊ฐ€ ํ›จ์”ฌ ๋” ์‰ฝ์Šต๋‹ˆ๋‹ค.

@gavinking ์ด๋Ÿฐ ๋œป์ธ๊ฐ€์š”?

Author reference = session.getReference( Author.class, authorId );
return Mutiny.fetch( reference.getBooks() );

๋‹น์—ฐํ•˜์ง€๋งŒ ์ €๋Š” ๋” ์‰ฝ๊ฒŒ ๋งŒ๋“ค์—ˆ์Šต๋‹ˆ๋‹ค. ๋ถ„๋ฆฌ๋œ ์ž‘์„ฑ์ž๋ฅผ getReference() ์ง์ ‘ ์ „๋‹ฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ข‹์•„, ๋‚˜๋Š” ๊ทธ ์ œ์•ˆ๋“ค์„ ์‹œ๋„ํ–ˆ์ง€๋งŒ ๋ถˆํ–‰ํžˆ๋„ ๊ทธ๊ฒƒ๋“ค๋„ ์ž‘๋™ํ•˜์ง€ ์•Š์•˜๋‹ค. ๋‚ด๊ฐ€ ์—ฌ๊ธฐ์—์„œ ๋ญ”๊ฐ€ ์ž˜๋ชปํ•˜๊ณ  ์žˆ๋‹ค๋ฉด ์นœ์ ˆํ•˜๊ฒŒ ์ง€์ ํ•ด ์ฃผ์‹ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

https://github.com/markusdlugi/hibernate-reactive-lazy-fetching/blob/f3458eee969d7c3597560aaf8a646b7aad2ee9b0/src/main/java/com/example/TestResource.java#L55 -L79

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)
    ...

์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. ๋ฐ”์ดํŠธ์ฝ”๋“œ ํ–ฅ์ƒ๊ณผ ๊ด€๋ จ์ด ์žˆ๋Š” ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.

@DavidD , ์—…๋ฐ์ดํŠธ๊ฐ€ ์žˆ์Šต๋‹ˆ๊นŒ?

์ด repo์—์„œ session-example ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ Quarkus ์—†์ด ๋ฌธ์ œ๋ฅผ ์žฌํ˜„ํ•˜๋ ค๊ณ  ํ–ˆ์ง€๋งŒ ๊ทธ๋ ‡๊ฒŒ ํ•  ์ˆ˜ ์—†์—ˆ์Šต๋‹ˆ๋‹ค. Hibernate Reactive์™€ ์ผ๋ฐ˜ Mutiny๋งŒ ์žˆ์œผ๋ฉด ์˜ˆ์ƒ๋Œ€๋กœ ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค. ๋˜ํ•œ find() , getReference() ๋˜๋Š” StatelessSession ํ•„์š” ์—†์ด ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค.

๋”ฐ๋ผ์„œ ์ด ๋ฌธ์ œ๋Š” Quarkus์—์„œ๋งŒ ๋ฐœ์ƒํ•˜๋Š” ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.

์•„์ง ์กฐ์‚ฌ ์ค‘์ž…๋‹ˆ๋‹ค.

Quarkus์—์„œ ๊ธฐ๋ณธ์ ์œผ๋กœ ํ™œ์„ฑํ™”๋œ ๋ฐ”์ดํŠธ์ฝ”๋“œ ํ–ฅ์ƒ ๋•Œ๋ฌธ์— ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.
Hibernate Reactive๋งŒ ์‚ฌ์šฉํ•  ๋•Œ ์–ด๋–ค ์†์„ฑ ์ง‘ํ•ฉ์ด ๋ฌธ์ œ๋ฅผ ์žฌํ˜„ํ•˜๋Š”์ง€ ์•„์ง ํŒŒ์•…ํ•˜์ง€ ๋ชปํ–ˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ๋‚˜๋Š” ์˜ค๋Š˜๋„ ๊ณ„์† ์ผํ•  ๊ฒƒ์ด๋‹ค

์ด์ œ ๋ฐ˜์‘ํ˜•์—์„œ ์˜ค๋ฅ˜๋ฅผ ์žฌํ˜„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋‘ ๊ฐ€์ง€๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

  1. hibernate.bytecode.allow_enhancement_as_proxy ๋ฅผ true๋กœ ์„ค์ •
  2. SessionFactoryOptions#enableCollectionInDefaultFetchGroup ๋ฅผ false๋กœ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค. true๋กœ ์„ค์ •ํ•ด์•ผํ•ฉ๋‹ˆ๋‹ค https://github.com/hibernate/hibernate-reactive/blob/a3f4c61eef71baab8ca321da5af0bfb9052bde4b/hibernate-reactive-core/src/main/java/org/hibernate/reactive/provider/service/ReactiveSessionFactoryBuilderService.java #L27

@gavinking Quarkus์—์„œ enableCollectionInDefaultFetchGroup ๊ฐ€ false๋กœ ์„ค์ •๋˜์–ด ์žˆ๋Š”์ง€ ํ™•์ธํ•ด์•ผ ํ•œ๋‹ค๊ณ  ์ƒ๊ฐํ•˜๋Š” ๊ฒƒ์ด ๋งž์Šต๋‹ˆ๊นŒ? ๋‚˜๋Š” ํ˜„์žฌ ์ „์ฒด ๋ฌธ์ œ๋ฅผ ์™„์ „ํžˆ ์ดํ•ดํ•˜์ง€ ๋ชปํ•œ๋‹ค๋Š” ๊ฒƒ์„ ์ธ์ •ํ•ฉ๋‹ˆ๋‹ค(๋‚˜๋Š” ๋ฌธ์ œ #374์— ๋Œ€ํ•œ ๋Œ€ํ™”๋ฅผ ๋”ฐ๋ฅด๋ ค๊ณ  ๋…ธ๋ ฅํ–ˆ์Šต๋‹ˆ๋‹ค)

์Œ. ๊ธฐ๋ณธ ๊ฐ€์ ธ์˜ค๊ธฐ ๊ทธ๋ฃน์— ์ปฌ๋ ‰์…˜์ด ์žˆ์–ด์•ผ๋งŒ ์˜๋ฏธ๊ฐ€ ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. ๋‚˜๋Š” ๊ทธ๊ฒƒ์„ ๋ฐฐ์ œํ•˜๋Š” ๊ฒƒ์ด ๊ฒฐ์ฝ” ์˜๋ฏธ๊ฐ€ ์—†๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.

n Quarkus enableCollectionInDefaultFetchGroup์ด false๋กœ ์„ค์ •๋จ

์ฃ„์†กํ•ฉ๋‹ˆ๋‹ค. ์˜ˆ, is set to true ๋ผ๊ณ  ์“ฐ๊ณ  ์‹ถ์—ˆ์Šต๋‹ˆ๋‹ค. ์ž ์‹œ ์‰ฌ์–ด๊ฐ€๋Š” ์‹œ๊ฐ„ :-)

์•„ ๊ทธ๋ ‡๊ตฐ์š” :-) ์‰ฌ์„ธ์š” :-)

@DavidD ํ›Œ๋ฅญํ•œ ๋ฐœ๊ฒฌ์ž…๋‹ˆ๋‹ค. ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค :)

#374 ๋ฐ ๊ด€๋ จ hibernate/hibernate-orm#3558์˜ ํ† ๋ก ์— ๋”ฐ๋ฅด๋ฉด enableCollectionInDefaultFetchGroup ๊ฐ€ Quarkus์—์„œ ์˜๋„์ ์œผ๋กœ false ๋กœ ๋‚จ๊ฒจ์ง„ ๊ฒƒ์ฒ˜๋Ÿผ ๋ณด์ธ๋‹ค๋Š” ์ ์„ ์ง€์ ํ•˜๊ณ  ์‹ถ์—ˆ์Šต๋‹ˆ๋‹ค. ์ ์–ด๋„ ๊ทธ๊ฒƒ์ด ๋‚ด๊ฐ€ ์ด ๋Œ“๊ธ€์—์„œ ์ˆ˜์ง‘ํ•˜๊ณ  ์žˆ๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

https://github.com/hibernate/hibernate-orm/pull/3558#issuecomment -695003875

์–ด์จŒ๋“  Quarkus๋Š” ํ˜„์žฌ ํ•ด๋‹น ์†์„ฑ์„ true ์„ค์ •ํ•˜์ง€ ์•Š๋Š” ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค. FastBootReactiveEntityManagerFactoryBuilder ์—์„œ ๊ตฌ์„ฑ๋˜์ง€ ์•Š์•˜๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

https://github.com/quarkusio/quarkus/blob/55cf15173ff7b2985a8422de050d1c9c708be57c/extensions/hibernate-reactive/runtime/src/main/java/io/quarkus/hibernate/reactive/runtime/boot/Entity#

๊ทธ๋ฆฌ๊ณ  ๋‚ด๊ฐ€ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ํ™•์ธํ–ˆ๋‹ค๋ฉด Hibernate ORM์˜ ๊ธฐ๋ณธ๊ฐ’์€ ์—ฌ์ „ํžˆ false ์ธ ๊ฒƒ ๊ฐ™์œผ๋ฉฐ ์ด๋Š” Quarkus์—์„œ๋งŒ ์ด ๋™์ž‘์„ ๋ณผ ์ˆ˜ ์žˆ๋Š” ์ด์œ ๋ฅผ ์„ค๋ช…ํ•ฉ๋‹ˆ๋‹ค.

@Sanne ์€ ์ด๊ฒƒ์„ ๋ด์•ผํ•ฉ๋‹ˆ๋‹ค.

true๋กœ ์„ค์ •ํ•˜๊ธฐ๋งŒ ํ•˜๋ฉด ๋งค์šฐ ์‰ฝ๊ฒŒ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ˆ˜์ • ์‚ฌํ•ญ์„ ์ž‘์„ฑํ•œ ๋‹ค์Œ @Sanne์—๊ฒŒ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.

๋‚ด๊ฐ€ ๊ถ๊ธˆํ•œ ๊ฒƒ์€ ๊ทธ๊ฒƒ์ด ORM์ด ํ•„์š”๋กœ ํ•˜๋Š” ๊ฒƒ์ด๊ธฐ ๋•Œ๋ฌธ์— false๋กœ ์„ค์ •๋˜์–ด ์žˆ๋Š”์ง€, ๊ทธ๋ฆฌ๊ณ  ์ผ๋ฐ˜ Hibernate๋ฅผ ๋ฐŸ์ง€ ์•Š๋„๋ก ์ฃผ์˜ํ•ด์•ผ ํ•˜๋Š”์ง€ ์—ฌ๋ถ€์ž…๋‹ˆ๋‹ค.

์ข‹์€ ๋ฐœ๊ฒฌ, ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค. ์˜ˆ, ๋‘ ๊ฐœ์˜ ํ™•์žฅ("์ผ๋ฐ˜" ๋ฐ "๋ฐ˜์‘")์ด ๊ณ ๋„๋กœ ๊ฒฐํ•ฉ๋œ ATM์ด๊ธฐ ๋•Œ๋ฌธ์— ์•ฝ๊ฐ„ ๊นŒ๋‹ค๋กญ์Šต๋‹ˆ๋‹ค.

๊ทธ๊ฒƒ๋“ค์„ ์ ์ ˆํ•˜๊ฒŒ ๋ถ„๋ฆฌํ•  ํ•„์š”๊ฐ€ ์žˆ๋‹ค, ๊ทธ๊ฒƒ์€ ๋„ˆ๋ฌด ์„œ๋‘๋ฅด๊ฒŒ ๊ฐœ๋ฐœ๋˜์—ˆ๋‹ค.

(OTOH, ORM์— ์™œ ORM์ด ํ•„์š”ํ•œ์ง€ ์ž˜ ๋ชจ๋ฅด๊ฒ ์Šต๋‹ˆ๋‹ค. ์ด์ „์— ์ฃผ์žฅํ–ˆ๋“ฏ์ด ๊ธฐ๋ณธ์ ์œผ๋กœ ์ผœ์ ธ ์žˆ์–ด์•ผ ํ•œ๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.)

ํ  ์ข‹์•„. ๋‘ ORM(Quarkus์—์„œ) ๋ชจ๋‘์— ๋Œ€ํ•ด ์ด ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

@gavinking , ์ œ๋ชฉ์ด ์ž˜๋ชป๋œ ๊ฒƒ ๊ฐ™์€๋ฐ์š”? ๋Œ€์‹  enableCollectionInDefaultFetchGroup ๋ฅผ ๋„ฃ์œผ๋ ค๊ณ  ํ–ˆ๋˜ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค. ์–ด์จŒ๋“  ๋„ค์ดํ‹ฐ๋ธŒ ๋•Œ๋ฌธ์— Quarkus์—๋Š” ๋ฐ”์ดํŠธ ์ฝ”๋“œ ํ–ฅ์ƒ์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. :)

์•„, ๋„ค, ๋„ค ๋ง์ด ๋งž๋Š” ๊ฒƒ ๊ฐ™์•„์š”.

Quarkus์˜ ๊ธฐ๋ณธ ์„ค์ •์— ์ด ๋ฒ„๊ทธ๊ฐ€ ์žˆ๋‹ค๋Š” ๋ง์”€์ด์‹ ๊ฐ€์š”?

์˜ˆ, ์ด ๋ฒ„๊ทธ๋ฅผ ์–ป๊ธฐ ์œ„ํ•ด Quarkus์—์„œ ํŠน๋ณ„ํ•œ ๊ตฌ์„ฑ์ด ํ•„์š”ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. @DavidD ๋Š”

ํ™•์‹ ํ•˜๋Š”.

๋ฌธ์ œ๋Š” ์‹ค์ œ๋กœ HR์˜ ๋ฒ„๊ทธ๊ฐ€ ์•„๋‹ˆ๋ผ๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ €๋Š” HR์—์„œ ํ•ญ์ƒ ํ•„์š”ํ•˜๊ธฐ ๋•Œ๋ฌธ์— "๊ธฐ๋ณธ ๊ฐ€์ ธ์˜ค๊ธฐ ๊ทธ๋ฃน์˜ ์ปฌ๋ ‰์…˜"์„ ์ฝ”์–ด์— ์˜๋„์ ์œผ๋กœ ์ถ”๊ฐ€ํ–ˆ์Šต๋‹ˆ๋‹ค.

๊ทธ๋ž˜์„œ ๋ฒ„๊ทธ๋Š” ๊ทธ๊ฒƒ์„ ๋„๋Š” Quarkus ํ™•์žฅ์— ์žˆ์Šต๋‹ˆ๋‹ค.

(๋˜๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ ์ž˜๋ชป๋œ ์ผ์„ ํ•˜๋Š” ํ•ต์‹ฌ์ž…๋‹ˆ๋‹ค.)

Quarkus์— ๋Œ€ํ•œ ์ˆ˜์ • ์‚ฌํ•ญ์„ ๋ณด๋ƒˆ์Šต๋‹ˆ๋‹ค: https://github.com/quarkusio/quarkus/pull/15818
getReference ๋Š” ํ˜„์žฌ quarkus์—์„œ ์ž‘๋™ํ•˜์ง€ ์•Š๋Š” ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค. ๋‚˜๋Š” ๊ทธ๊ฒƒ์— ๋Œ€ํ•ด ๋ณ„๋„์˜ ๋ฌธ์ œ๋ฅผ ๋งŒ๋“ค ๊ฒƒ์ž…๋‹ˆ๋‹ค.

์ž˜ ๋“ค๋ฆฝ๋‹ˆ๋‹ค. ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค! ๐Ÿ‘

๋ฐฉ๊ธˆ ํ…Œ์ŠคํŠธํ–ˆ๊ณ  ์ด์ œ Quarkus์—์„œ ์ง€์—ฐ ์ปฌ๋ ‰์…˜ ๊ฐ€์ ธ์˜ค๊ธฐ๊ฐ€ ์ž‘๋™ํ•˜๋Š”์ง€ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋‹ค์‹œ ํ•œ ๋ฒˆ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค :์Šค๋งˆ์ผ:

@markusdlugi ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค.

์‹ ๊ณ ํ•ด ์ฃผ์…”์„œ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค @markusdlugi

์ด ํŽ˜์ด์ง€๊ฐ€ ๋„์›€์ด ๋˜์—ˆ๋‚˜์š”?
0 / 5 - 0 ๋“ฑ๊ธ‰

๊ด€๋ จ ๋ฌธ์ œ

arifpratama398 picture arifpratama398  ยท  10์ฝ”๋ฉ˜ํŠธ

yaakov-berkovitch picture yaakov-berkovitch  ยท  16์ฝ”๋ฉ˜ํŠธ

gavinking picture gavinking  ยท  6์ฝ”๋ฉ˜ํŠธ

Xset-s picture Xset-s  ยท  3์ฝ”๋ฉ˜ํŠธ

tsegismont picture tsegismont  ยท  9์ฝ”๋ฉ˜ํŠธ