hibernate / hibernate-reactive

A reactive API for Hibernate ORM, supporting non-blocking database drivers and a reactive style of interaction with the database.
https://hibernate.org/reactive
Apache License 2.0
441 stars 92 forks source link

`ClassCastException` when retrieving composite table entity with `@IdClass` #2004

Closed janek64 closed 1 month ago

janek64 commented 1 month ago

When I retrieve an entity that models a composite table/relationship between two other tables and uses an @IdClass to implement the composite primary key, the query fails with a ClassCastException. This issue is similar to the failure reported in #1979 (which I also commented on), but not resolved by 2.4.1.Final.

As an example, I use the following (abbreviated) entities:

@Entity
@Table(name = "entity_a")
public class EntityA {

    @Id
    @Column(name = "id")
    private UUID id;

    @Column(name = "name")
    private String name;

    // Constructor, Getter and Setter
}
@Entity
@Table(name = "entity_b")
public class EntityB {

    @Id
    @Column(name = "id")
    private UUID id;

    @Column(name = "name")
    private String name;

    // Constructor, Getter and Setter
}

The entities are connected via the following composite table which uses an @IdClass:

@Entity
@Table(name = "relationship")
@IdClass(RelationshipId.class)
public class Relationship {

    @Id
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "a_id", referencedColumnName = "id")
    private EntityA entityA;

    @Id
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "b_id", referencedColumnName = "id")
    private EntityB entityB;

    @Column(name = "dataField")
    private String dataField;

    // Constructor, Getter, Setter, Equal, HashCode
}
public class RelationshipId implements Serializable {

    private UUID entityA;
    private UUID entityB;

    // Constructor, Getter, Setter, Equal, HashCode
}

The EntityA and EntityB class can be persisted without issues. The Relationship class, can be persisted, but not retrieved. When performing a query such as

session.createQuery("SELECT r FROM Relationship r", Relationship.class).getResultList()

the following exception is thrown:

11:47:47.417 [vert.x-eventloop-thread-16] INFO  o.h.r.v.impl.DefaultVertxInstance - HR000003: Vert.x instance stopped
Exception in thread "main" java.util.concurrent.CompletionException: java.lang.ClassCastException: class org.hibernate.sql.results.graph.entity.internal.EntityDelayedFetchInitializer cannot be cast to class org.hibernate.reactive.sql.results.graph.ReactiveInitializer (org.hibernate.sql.results
.graph.entity.internal.EntityDelayedFetchInitializer and org.hibernate.reactive.sql.results.graph.ReactiveInitializer are in unnamed module of loader 'app')
        at java.base/java.util.concurrent.CompletableFuture.encodeThrowable(CompletableFuture.java:332)
        at java.base/java.util.concurrent.CompletableFuture.uniComposeStage(CompletableFuture.java:1177)
        at java.base/java.util.concurrent.CompletableFuture.thenCompose(CompletableFuture.java:2341)
        at java.base/java.util.concurrent.CompletableFuture.thenCompose(CompletableFuture.java:144)
        at org.hibernate.reactive.util.async.impl.AsyncTrampoline.asyncWhile(AsyncTrampoline.java:216)
        at org.hibernate.reactive.util.impl.CompletionStages.loop(CompletionStages.java:411)
        at org.hibernate.reactive.util.impl.CompletionStages.loop(CompletionStages.java:382)
        at org.hibernate.reactive.util.impl.CompletionStages.loop(CompletionStages.java:175)
        at org.hibernate.reactive.sql.results.graph.embeddable.internal.ReactiveNonAggregatedIdentifierMappingInitializer.reactiveResolveKey(ReactiveNonAggregatedIdentifierMappingInitializer.java:64)
        at org.hibernate.reactive.sql.results.graph.embeddable.internal.ReactiveNonAggregatedIdentifierMappingInitializer.reactiveResolveKey(ReactiveNonAggregatedIdentifierMappingInitializer.java:25)
        at org.hibernate.reactive.sql.results.graph.entity.internal.ReactiveEntityInitializerImpl.initializeId(ReactiveEntityInitializerImpl.java:749)
        at org.hibernate.reactive.sql.results.graph.entity.internal.ReactiveEntityInitializerImpl.reactiveResolveKey(ReactiveEntityInitializerImpl.java:625)
        at org.hibernate.reactive.sql.results.graph.entity.internal.ReactiveEntityInitializerImpl.reactiveResolveKey(ReactiveEntityInitializerImpl.java:605)
        at org.hibernate.reactive.sql.results.graph.entity.internal.ReactiveEntityInitializerImpl.reactiveResolveKey(ReactiveEntityInitializerImpl.java:56)
        at org.hibernate.reactive.sql.results.internal.ReactiveStandardRowReader.resolveKey(ReactiveStandardRowReader.java:383)
        at org.hibernate.reactive.sql.results.internal.ReactiveStandardRowReader.lambda$coordinateInitializers$28(ReactiveStandardRowReader.java:376)
        at org.hibernate.reactive.util.impl.CompletionStages.lambda$loop$7(CompletionStages.java:410)
        at org.hibernate.reactive.util.impl.CompletionStages$ArrayLoop.next(CompletionStages.java:484)
        at org.hibernate.reactive.util.async.impl.AsyncTrampoline.lambda$asyncWhile$1(AsyncTrampoline.java:215)
        at org.hibernate.reactive.util.async.impl.AsyncTrampoline$TrampolineInternal.unroll(AsyncTrampoline.java:121)
        at org.hibernate.reactive.util.async.impl.AsyncTrampoline$TrampolineInternal.trampoline(AsyncTrampoline.java:102)
        at org.hibernate.reactive.util.async.impl.AsyncTrampoline.asyncWhile(AsyncTrampoline.java:197)
        at org.hibernate.reactive.util.async.impl.AsyncTrampoline.asyncWhile(AsyncTrampoline.java:215)
        at org.hibernate.reactive.util.impl.CompletionStages.loop(CompletionStages.java:411)
        at org.hibernate.reactive.util.impl.CompletionStages.loop(CompletionStages.java:382)
        at org.hibernate.reactive.sql.results.internal.ReactiveStandardRowReader.coordinateInitializers(ReactiveStandardRowReader.java:376)        
        at org.hibernate.reactive.sql.results.internal.ReactiveStandardRowReader.reactiveReadRow(ReactiveStandardRowReader.java:126)
        at org.hibernate.reactive.sql.results.spi.ReactiveListResultsConsumer.lambda$addToResultsSupplier$5(ReactiveListResultsConsumer.java:141)  
        at org.hibernate.reactive.sql.results.spi.ReactiveListResultsConsumer.lambda$consume$1(ReactiveListResultsConsumer.java:96)
        at java.base/java.util.concurrent.CompletableFuture$UniCompose.tryFire(CompletableFuture.java:1150)
        at java.base/java.util.concurrent.CompletableFuture.postComplete(CompletableFuture.java:510)
        at java.base/java.util.concurrent.CompletableFuture.complete(CompletableFuture.java:2179)
        at io.vertx.core.Future.lambda$toCompletionStage$3(Future.java:601)
        at io.vertx.core.impl.future.FutureImpl$4.onSuccess(FutureImpl.java:176)
        at io.vertx.core.impl.future.FutureBase.emitSuccess(FutureBase.java:66)
        at io.vertx.core.impl.future.FutureImpl.tryComplete(FutureImpl.java:259)
        at io.vertx.sqlclient.impl.QueryResultBuilder.tryComplete(QueryResultBuilder.java:88)
        at io.vertx.sqlclient.impl.QueryResultBuilder.tryComplete(QueryResultBuilder.java:32)
        at io.vertx.core.Promise.complete(Promise.java:66)
        at io.vertx.core.Promise.handle(Promise.java:51)
        at io.vertx.core.Promise.handle(Promise.java:29)
        at io.vertx.core.impl.future.FutureImpl$4.onSuccess(FutureImpl.java:176)
        at io.vertx.core.impl.future.FutureBase.lambda$emitSuccess$0(FutureBase.java:60)
        at io.vertx.core.impl.ContextImpl.execute(ContextImpl.java:298)
        at io.vertx.core.impl.DuplicatedContext.execute(DuplicatedContext.java:169)
        at io.vertx.core.impl.future.FutureBase.emitSuccess(FutureBase.java:57)
        at io.vertx.core.impl.future.FutureImpl.tryComplete(FutureImpl.java:259)
        at io.vertx.core.impl.future.PromiseImpl.onSuccess(PromiseImpl.java:49)
        at io.vertx.core.impl.future.PromiseImpl.handle(PromiseImpl.java:41)
        at io.vertx.core.impl.future.PromiseImpl.handle(PromiseImpl.java:23)
        at io.vertx.sqlclient.impl.command.CommandResponse.fire(CommandResponse.java:46)
        at io.vertx.sqlclient.impl.SocketConnectionBase.handleMessage(SocketConnectionBase.java:324)
        at io.vertx.pgclient.impl.PgSocketConnection.handleMessage(PgSocketConnection.java:114)
        at io.vertx.sqlclient.impl.SocketConnectionBase.lambda$init$0(SocketConnectionBase.java:137)
        at io.vertx.core.impl.ContextImpl.emit(ContextImpl.java:328)
        at io.vertx.core.impl.ContextImpl.emit(ContextImpl.java:321)
        at io.vertx.core.net.impl.NetSocketImpl.handleMessage(NetSocketImpl.java:388)
        at io.vertx.core.net.impl.ConnectionBase.read(ConnectionBase.java:159)
        at io.vertx.core.net.impl.VertxHandler.channelRead(VertxHandler.java:153)
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:442)
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420)
        at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:412)
        at io.netty.channel.CombinedChannelDuplexHandler$DelegatingChannelHandlerContext.fireChannelRead(CombinedChannelDuplexHandler.java:436)    
        at io.vertx.pgclient.impl.codec.PgDecoder.fireCommandResponse(PgDecoder.java:52)
        at io.vertx.pgclient.impl.codec.PgCommandCodec.handleReadyForQuery(PgCommandCodec.java:137)
        at io.vertx.pgclient.impl.codec.PgDecoder.decodeReadyForQuery(PgDecoder.java:248)
        at io.vertx.pgclient.impl.codec.PgDecoder.channelRead(PgDecoder.java:107)
        at io.netty.channel.CombinedChannelDuplexHandler.channelRead(CombinedChannelDuplexHandler.java:251)
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:442)
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420)
        at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:412)
        at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1407)
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:440)
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420)
        at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:918)
        at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:166)
        at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:788)
        at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:724)
        at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:650)
        at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:562)
        at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:994)
        at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
        at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
        at java.base/java.lang.Thread.run(Thread.java:1583)
Caused by: java.lang.ClassCastException: class org.hibernate.sql.results.graph.entity.internal.EntityDelayedFetchInitializer cannot be cast to clas
s org.hibernate.reactive.sql.results.graph.ReactiveInitializer (org.hibernate.sql.results.graph.entity.internal.EntityDelayedFetchInitializer and org.hibernate.reactive.sql.results.graph.ReactiveInitializer are in unnamed module of loader 'app')
        at org.hibernate.reactive.sql.results.graph.embeddable.internal.ReactiveNonAggregatedIdentifierMappingInitializer.lambda$reactiveResolveKey$1(ReactiveNonAggregatedIdentifierMappingInitializer.java:68)
        at org.hibernate.reactive.util.impl.CompletionStages.lambda$loop$2(CompletionStages.java:175)
        at org.hibernate.reactive.util.impl.CompletionStages.lambda$loop$7(CompletionStages.java:410)
        at org.hibernate.reactive.util.impl.CompletionStages$ArrayLoop.next(CompletionStages.java:484)
        at org.hibernate.reactive.util.async.impl.AsyncTrampoline.lambda$asyncWhile$1(AsyncTrampoline.java:215)
        at org.hibernate.reactive.util.async.impl.AsyncTrampoline$TrampolineInternal.unroll(AsyncTrampoline.java:121)
        at org.hibernate.reactive.util.async.impl.AsyncTrampoline$TrampolineInternal.trampoline(AsyncTrampoline.java:102)
        at org.hibernate.reactive.util.async.impl.AsyncTrampoline.asyncWhile(AsyncTrampoline.java:197)
        at org.hibernate.reactive.util.async.impl.AsyncTrampoline.asyncWhile(AsyncTrampoline.java:215)
        ... 79 more

When using Hibernate Reactive 2.3.1.Final, the entities can be retrieved. In 2.4.0.Final and 2.4.1.Final, the exception occurs. Based on the previous ticket, I created the following reproducer of the entities shown above that demonstrates the issue: hibernate-reactive-idclass-issue.zip

You can execute it with ./gradlew run. If you want to inspect the behaviour in the previous version, you can adjust the ext block in the build.gradle.

Best regards, Janek

DavideD commented 1 month ago

Thanks, I will have a look