hibernate-reactive: UnexpectedAccessToTheDatabase when querying entity with IdClass with association

When you have an entity with a composite identifier that has an association to another entity (e.g., like in Example 134. IdClass with @ManyToOne in the Hibernate User Guide) then any attempt to query such entities will result in an exception.

So given for example the following entities:

@Entity
public class GroceryList implements Serializable {

    @Id @GeneratedValue
    private Long id;

    @OneToMany(mappedBy = "groceryList", fetch = FetchType.LAZY, cascade = CascadeType.ALL, orphanRemoval = true)
    private List<ShoppingItem> shoppingItems;

}
@Entity
@IdClass(ShoppingItem.ShoppingItemId.class)
public class ShoppingItem implements Serializable {

    @Id
    @ManyToOne
    @JoinColumn(name = "grocerylistid")
    private GroceryList groceryList;

    @Id
    private String itemName;

    private int itemCount;

    public static class ShoppingItemId implements Serializable {

        private String itemName;
        private GroceryList groceryList;
    }
}

When you try to run a simple query like this:

FROM ShoppingItem WHERE grocerylistid = :id

Then you will receive the following exception:

2022-09-06 14:32:55,518 ERROR [org.hib.rea.errors] (vert.x-eventloop-thread-2) HR000057: Failed to execute statement [select shoppingit0_.grocerylistid as groceryl3_1_, shoppingit0_.itemName as itemname1_1_, shoppingit0_.itemCount as itemcoun2_1_ from ShoppingItem shoppingit0_ where grocerylistid=$1]: could not execute query: java.util.concurrent.CompletionException: org.hibernate.reactive.event.impl.UnexpectedAccessToTheDatabase: Unexpected access to the database
	at java.base/java.util.concurrent.CompletableFuture.encodeThrowable(CompletableFuture.java:314)
	at java.base/java.util.concurrent.CompletableFuture.completeThrowable(CompletableFuture.java:319)
	at java.base/java.util.concurrent.CompletableFuture$UniCompose.tryFire(CompletableFuture.java:1081)
	at java.base/java.util.concurrent.CompletableFuture.postComplete(CompletableFuture.java:506)
	at java.base/java.util.concurrent.CompletableFuture.complete(CompletableFuture.java:2073)
	at io.vertx.core.Future.lambda$toCompletionStage$2(Future.java:360)
	at io.vertx.core.impl.future.FutureImpl$3.onSuccess(FutureImpl.java:141)
	at io.vertx.core.impl.future.FutureBase.emitSuccess(FutureBase.java:60)
	at io.vertx.core.impl.future.FutureImpl.tryComplete(FutureImpl.java:211)
	at io.vertx.core.impl.future.PromiseImpl.tryComplete(PromiseImpl.java:23)
	at io.vertx.sqlclient.impl.QueryResultBuilder.tryComplete(QueryResultBuilder.java:102)
	at io.vertx.sqlclient.impl.QueryResultBuilder.tryComplete(QueryResultBuilder.java:35)
	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$3.onSuccess(FutureImpl.java:141)
	at io.vertx.core.impl.future.FutureBase.emitSuccess(FutureBase.java:60)
	at io.vertx.core.impl.future.FutureImpl.tryComplete(FutureImpl.java:211)
	at io.vertx.core.impl.future.PromiseImpl.tryComplete(PromiseImpl.java:23)
	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.sqlclient.impl.TransactionImpl.lambda$wrap$0(TransactionImpl.java:72)
	at io.vertx.core.impl.future.FutureImpl$3.onSuccess(FutureImpl.java:141)
	at io.vertx.core.impl.future.FutureBase.lambda$emitSuccess$0(FutureBase.java:54)
	at io.netty.util.concurrent.AbstractEventExecutor.runTask(AbstractEventExecutor.java:174)
	at io.netty.util.concurrent.AbstractEventExecutor.safeExecute(AbstractEventExecutor.java:167)
	at io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks(SingleThreadEventExecutor.java:470)
	at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:503)
	at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:997)
	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:829)
Caused by: org.hibernate.reactive.event.impl.UnexpectedAccessToTheDatabase: Unexpected access to the database
	at org.hibernate.reactive.event.impl.DefaultReactiveLoadEventListener.onLoad(DefaultReactiveLoadEventListener.java:108)
	at org.hibernate.event.service.internal.EventListenerGroupImpl.fireEventOnEachListener(EventListenerGroupImpl.java:118)
	at org.hibernate.internal.SessionImpl.fireLoadNoChecks(SessionImpl.java:1231)
	at org.hibernate.internal.SessionImpl.internalLoad(SessionImpl.java:1096)
	at org.hibernate.type.EntityType.resolveIdentifier(EntityType.java:706)
	at org.hibernate.type.EntityType.resolve(EntityType.java:465)
	at org.hibernate.type.ManyToOneType.resolve(ManyToOneType.java:265)
	at org.hibernate.type.EntityType.resolve(EntityType.java:458)
	at org.hibernate.type.ComponentType.resolve(ComponentType.java:695)
	at org.hibernate.loader.Loader.extractKeysFromResultSet(Loader.java:881)
	at org.hibernate.loader.Loader.getRowFromResultSet(Loader.java:735)
	at org.hibernate.loader.Loader.getRowsFromResultSet(Loader.java:1047)
	at org.hibernate.reactive.loader.hql.impl.ReactiveQueryLoader.getRowsFromResultSet(ReactiveQueryLoader.java:223)
	at org.hibernate.reactive.loader.ReactiveLoaderBasedResultSetProcessor.reactiveExtractResults(ReactiveLoaderBasedResultSetProcessor.java:73)
	at org.hibernate.reactive.loader.hql.impl.ReactiveQueryLoader$1.reactiveExtractResults(ReactiveQueryLoader.java:72)
	at org.hibernate.reactive.loader.ReactiveLoader.reactiveProcessResultSet(ReactiveLoader.java:145)
	at org.hibernate.reactive.loader.ReactiveLoader.lambda$doReactiveQueryAndInitializeNonLazyCollections$0(ReactiveLoader.java:77)
	at java.base/java.util.concurrent.CompletableFuture$UniCompose.tryFire(CompletableFuture.java:1072)
	... 29 more

Similarly, a LEFT JOIN FETCH doesn’t work either. The only way I was able to work around this issue is to use an Entity Graph to fetch GroceryList + ShoppingItem. And of course, as soon as you get rid of the @IdClass, it also works.

Full Reproducer: https://github.com/markusdlugi/hr-composite-id-reproducer

Reproducer uses Hibernate Reactive Panache, but it is also reproducible with vanilla HR.

Tested with Quarkus 2.12.0.Final and Hibernate Reactive 1.1.7.Final.

About this issue

  • Original URL
  • State: open
  • Created 2 years ago
  • Comments: 17 (12 by maintainers)

Commits related to this issue

Most upvoted comments

Also FetchMode.JOIN doesn’t seem to work in this case (at least in my quick test), but this query will work:

s.createQuery( "select t from Teacher t left join fetch t.idDocumentType", Teacher.class ).getResultList()

Sorry about it, eventually we will fix these issues

We tend to keep the model for a unit test in the same file containing the tests. This means that the classes are inner classes and must be static for Hibernate to work. Gavin probably copied and pasted the classes that he’s used to verify that the workaround works. It’s not part of the workaround.

mmmm, this looks really hard, and I think it’s not worth fixing for now.

I’ve reproduced this in our test suite.