hibernate-reactive icon indicating copy to clipboard operation
hibernate-reactive copied to clipboard

Composite key + fetch join results in UnknownTableReferenceException

Open holomekc opened this issue 2 years ago • 5 comments

Hi we are currently starting with the migration to Quarkus 3,

We face some issues with our existing database structure and Hibernate 6. It seems to be the combination of a composite key and a fetch join. Due to the changes I checked the documentation and migration guide again in case we did something wrong or forgot something. I do not see it :disappointed: . I created a reproducer here:

quarkus-quickstarts/hibernate-reactive-panache-quickstart at main · holomekc/quarkus-quickstarts

But maybe here in short as well:

@Entity
@Table(name = "fruit_basket")
public class FruitBasket extends PanacheEntity {

    @Column
    public String name;

    @OneToMany(mappedBy = "basket", fetch = FetchType.LAZY)
    public Collection<Fruit> fruits;
}
@Entity
@IdClass(FruitId.class)
@Table(name = "fruit")
public class Fruit extends PanacheEntityBase {

    @Id
    @GeneratedValue
    public Long id;

    @Id
    @JoinColumn(name = "basket_id", referencedColumnName = "id", foreignKey = @ForeignKey(name = "basket_fk"))
    @ManyToOne(fetch = FetchType.LAZY)
    public FruitBasket basket;

    @Column(length = 40, unique = true)
    public String name;


    public Fruit() {
    }

    public Fruit(String name) {
        this.name = name;
    }
}
public class FruitId implements Serializable {

    private Long id;
    private FruitBasket basket;

    public Long getId() {
        return id;
    }

    public FruitId setId(Long id) {
        this.id = id;
        return this;
    }

    public FruitBasket getBasket() {
        return basket;
    }

    public FruitId setBasket(FruitBasket basket) {
        this.basket = basket;
        return this;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }
        FruitId fruitId = (FruitId) o;
        return Objects.equals(id, fruitId.id) && Objects.equals(basket, fruitId.basket);
    }

    @Override
    public int hashCode() {
        return Objects.hash(id, basket);
    }
}
2023-05-22 04:35:33,895 ERROR [org.acm.hib.orm.pan.FruitResource] (vert.x-eventloop-thread-2) Failed to handle request: org.hibernate.HibernateException: Could not generate fetch : org.acme.hibernate.orm.panache.Fruit(f) -> {id}
        at org.hibernate.query.sqm.sql.BaseSqmToSqlAstConverter.buildFetch(BaseSqmToSqlAstConverter.java:7466)
        at org.hibernate.query.sqm.sql.BaseSqmToSqlAstConverter.createFetch(BaseSqmToSqlAstConverter.java:7295)
        at org.hibernate.query.sqm.sql.BaseSqmToSqlAstConverter.visitIdentifierFetch(BaseSqmToSqlAstConverter.java:7143)
        at org.hibernate.sql.results.graph.entity.AbstractEntityResultGraphNode.afterInitialize(AbstractEntityResultGraphNode.java:63)
        at org.hibernate.reactive.persister.entity.impl.ReactiveAbstractPersisterDelegate.createDomainResult(ReactiveAbstractPersisterDelegate.java:106)
        at org.hibernate.reactive.persister.entity.impl.ReactiveSingleTableEntityPersister.createDomainResult(ReactiveSingleTableEntityPersister.java:105)
        at org.hibernate.query.sqm.sql.internal.AbstractSqmPathInterpretation.createDomainResult(AbstractSqmPathInterpretation.java:55)
        at org.hibernate.query.sqm.sql.BaseSqmToSqlAstConverter.lambda$visitSelection$23(BaseSqmToSqlAstConverter.java:2240)
        at java.base/java.util.Collections$SingletonList.forEach(Collections.java:4966)
        at org.hibernate.query.sqm.sql.BaseSqmToSqlAstConverter.visitSelection(BaseSqmToSqlAstConverter.java:2235)
        at org.hibernate.query.sqm.sql.BaseSqmToSqlAstConverter.visitSelectClause(BaseSqmToSqlAstConverter.java:2153)
        at org.hibernate.query.sqm.sql.BaseSqmToSqlAstConverter.visitQuerySpec(BaseSqmToSqlAstConverter.java:2021)
        at org.hibernate.query.sqm.sql.BaseSqmToSqlAstConverter.visitQuerySpec(BaseSqmToSqlAstConverter.java:425)
        at org.hibernate.query.sqm.tree.select.SqmQuerySpec.accept(SqmQuerySpec.java:122)
        at org.hibernate.query.sqm.spi.BaseSemanticQueryWalker.visitQueryPart(BaseSemanticQueryWalker.java:221)
        at org.hibernate.query.sqm.sql.BaseSqmToSqlAstConverter.visitQueryPart(BaseSqmToSqlAstConverter.java:1881)
        at org.hibernate.query.sqm.sql.BaseSqmToSqlAstConverter.visitSelectStatement(BaseSqmToSqlAstConverter.java:1566)
        at org.hibernate.query.sqm.sql.BaseSqmToSqlAstConverter.visitSelectStatement(BaseSqmToSqlAstConverter.java:425)
        at org.hibernate.query.sqm.tree.select.SqmSelectStatement.accept(SqmSelectStatement.java:222)
        at org.hibernate.query.sqm.sql.BaseSqmToSqlAstConverter.translate(BaseSqmToSqlAstConverter.java:759)
        at org.hibernate.reactive.query.sqm.iternal.ConcreteSqmSelectReactiveQueryPlan.buildCacheableSqmInterpretation(ConcreteSqmSelectReactiveQueryPlan.java:208)
        at org.hibernate.reactive.query.sqm.iternal.ConcreteSqmSelectReactiveQueryPlan.withCacheableSqmInterpretation(ConcreteSqmSelectReactiveQueryPlan.java:138)
        at org.hibernate.reactive.query.sqm.iternal.ConcreteSqmSelectReactiveQueryPlan.reactivePerformList(ConcreteSqmSelectReactiveQueryPlan.java:122)
        at org.hibernate.reactive.query.sqm.iternal.ReactiveQuerySqmImpl.doReactiveList(ReactiveQuerySqmImpl.java:200)
        at org.hibernate.reactive.query.spi.ReactiveAbstractSelectionQuery.doReactiveList(ReactiveAbstractSelectionQuery.java:287)
        at org.hibernate.reactive.query.spi.ReactiveAbstractSelectionQuery.reactiveList(ReactiveAbstractSelectionQuery.java:202)
        at org.hibernate.reactive.query.sqm.iternal.ReactiveQuerySqmImpl.reactiveList(ReactiveQuerySqmImpl.java:154)
        at org.hibernate.reactive.query.ReactiveSelectionQuery.getReactiveResultList(ReactiveSelectionQuery.java:42)
        at io.smallrye.context.impl.wrappers.SlowContextualSupplier.get(SlowContextualSupplier.java:21)
        at io.smallrye.mutiny.operators.uni.builders.UniCreateFromCompletionStage.subscribe(UniCreateFromCompletionStage.java:24)
        at io.smallrye.mutiny.operators.AbstractUni.subscribe(AbstractUni.java:36)
        at io.smallrye.mutiny.operators.uni.UniRunSubscribeOn.lambda$subscribe$0(UniRunSubscribeOn.java:27)
        at org.hibernate.reactive.context.impl.VertxContext.execute(VertxContext.java:90)
        at io.smallrye.mutiny.operators.uni.UniRunSubscribeOn.subscribe(UniRunSubscribeOn.java:25)
        at io.smallrye.mutiny.operators.AbstractUni.subscribe(AbstractUni.java:36)
        at io.smallrye.mutiny.operators.uni.UniOnItemTransformToUni$UniOnItemTransformToUniProcessor.performInnerSubscription(UniOnItemTransformToUni.java:81)
        at io.smallrye.mutiny.operators.uni.UniOnItemTransformToUni$UniOnItemTransformToUniProcessor.onItem(UniOnItemTransformToUni.java:57)
        at io.smallrye.mutiny.operators.uni.UniOnItemConsume$UniOnItemComsumeProcessor.onItem(UniOnItemConsume.java:43)
        at io.smallrye.mutiny.operators.uni.UniOnItemTransform$UniOnItemTransformProcessor.onItem(UniOnItemTransform.java:43)
        at io.smallrye.mutiny.operators.uni.UniOnItemTransformToUni$UniOnItemTransformToUniProcessor.onItem(UniOnItemTransformToUni.java:60)
        at io.smallrye.mutiny.operators.uni.UniOperatorProcessor.onItem(UniOperatorProcessor.java:47)
        at io.smallrye.mutiny.operators.uni.builders.UniCreateFromItemSupplier.subscribe(UniCreateFromItemSupplier.java:29)
        at io.smallrye.mutiny.operators.AbstractUni.subscribe(AbstractUni.java:36)
        at io.smallrye.mutiny.operators.uni.UniOnFailureFlatMap.subscribe(UniOnFailureFlatMap.java:31)
        at io.smallrye.mutiny.operators.AbstractUni.subscribe(AbstractUni.java:36)
        at io.smallrye.mutiny.operators.uni.UniOnItemTransformToUni$UniOnItemTransformToUniProcessor.performInnerSubscription(UniOnItemTransformToUni.java:81)
        at io.smallrye.mutiny.operators.uni.UniOnItemTransformToUni$UniOnItemTransformToUniProcessor.onItem(UniOnItemTransformToUni.java:57)
        at io.smallrye.mutiny.operators.uni.UniOperatorProcessor.onItem(UniOperatorProcessor.java:47)
        at io.smallrye.mutiny.operators.uni.builders.UniCreateFromCompletionStage$CompletionStageUniSubscription.forwardResult(UniCreateFromCompletionStage.java:63)
        at java.base/java.util.concurrent.CompletableFuture.uniWhenComplete(CompletableFuture.java:863)
        at java.base/java.util.concurrent.CompletableFuture$UniWhenComplete.tryFire(CompletableFuture.java:841)
        at java.base/java.util.concurrent.CompletableFuture.postComplete(CompletableFuture.java:510)
        at java.base/java.util.concurrent.CompletableFuture.complete(CompletableFuture.java:2147)
        at io.vertx.core.Future.lambda$toCompletionStage$3(Future.java:384)
        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.Mapping.onSuccess(Mapping.java:40)
        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:569)
        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:833)
Caused by: org.hibernate.HibernateException: Could not generate fetch : org.acme.hibernate.orm.panache.Fruit(f).{id}(598220203321057) -> basket
        at org.hibernate.query.sqm.sql.BaseSqmToSqlAstConverter.buildFetch(BaseSqmToSqlAstConverter.java:7466)
        at org.hibernate.query.sqm.sql.BaseSqmToSqlAstConverter.createFetch(BaseSqmToSqlAstConverter.java:7295)
        at org.hibernate.query.sqm.sql.BaseSqmToSqlAstConverter.visitFetches(BaseSqmToSqlAstConverter.java:7412)
        at org.hibernate.sql.results.graph.AbstractFetchParent.afterInitialize(AbstractFetchParent.java:32)
        at org.hibernate.sql.results.graph.embeddable.internal.EmbeddableFetchImpl.<init>(EmbeddableFetchImpl.java:75)
        at org.hibernate.metamodel.internal.AbstractCompositeIdentifierMapping.geetch(AbstractCompositeIdentifierMapping.java:109)
        at org.hibernate.sql.results.graph.FetchParent.generateFetchableFetch(FetchParent.java:108)
        at org.hibernate.query.sqm.sql.BaseSqmToSqlAstConverter.buildFetch(BaseSqmToSqlAstConverter.java:7455)
        ... 66 more
Caused by: org.hibernate.sql.ast.tree.from.UnknownTableReferenceException: Unable to determine TableReference (`fruit`) for `org.acme.hibernate.orm.panache.Fruit(f).{id}(598220203321057).basket.{fk}`
        at org.hibernate.sql.ast.tree.from.LazyTableGroup.resolveTableReference(LazyTableGroup.java:256)
        at org.hibernate.sql.ast.tree.from.DelegatingTableGroup.resolveTableReference(DelegatingTableGroup.java:62)
        at org.hibernate.metamodel.mapping.internal.SimpleForeignKeyDescriptor.createDomainResult(SimpleForeignKeyDescriptor.java:305)
        at org.hibernate.metamodel.mapping.internal.SimpleForeignKeyDescriptor.createDomainResult(SimpleForeignKeyDescriptor.java:254)
        at org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping.generateFetch(ToOneAttributeMapping.java:1487)
        at org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping.generateFetch(ToOneAttributeMapping.java:107)
        at org.hibernate.sql.results.graph.FetchParent.generateFetchableFetch(FetchParent.java:108)
        at org.hibernate.query.sqm.sql.BaseSqmToSqlAstConverter.buildFetch(BaseSqmToSqlAstConverter.java:7455)
        ... 73 more

Br,

Chris

holomekc avatar May 31 '23 13:05 holomekc

We looked into this and it seems there are some issues related to composite id. In particular, we don't have a reactive implementation for CompositeNestedGeneratedValueGenerator. meaning that the generation of the id is not going to work.

I'm leaving a note here to keep track of it, but @blafond is checking if it's related or should be considered a separate issue.

DavideD avatar Mar 25 '24 16:03 DavideD

pushed some temporary reactive tests to explore the problem: https://github.com/blafond/hibernate-reactive/tree/issue_1646

Other notes....

  • The Quarkus test appears to persist the entities without error then fails on the fetch(...)
  • The Reactive id generator error is a separate issue.
    • Note: If the Fruit entity id is NOT generated (just set) then the reactive persist(...) works.

blafond avatar Mar 26 '24 17:03 blafond

@holomekc please see some changes I made to your test project on my branch: https://github.com/blafond/quarkus-quickstarts/commits/hr_issue_1646/

After working on the reactive tests (see above) filling out your entity definitions and adding some import sql seemed to help.

blafond avatar Apr 03 '24 19:04 blafond

@holomekc, this will work if you update to the latest quarkus version, and fix the name of the sequence in the import.sql from fruitbasket_seq to fruit_basket_seq.

You can see the changes I've made here: https://github.com/DavideD/quarkus-quickstarts/commit/114eee42be6ca09387668f04efab07957396190c

That said, it seems that persisting a new entity is not going to work. The problem is that in Hibernate Reactive we haven't implemented the required identity generator: CompositeNestedGeneratedValueGenerator

I cannot make any promises but I will try to fix it before the next release.

DavideD avatar Apr 10 '24 15:04 DavideD

Thx for all the updates!!! We will check asap. Currently we have a lot to do.

holomekc avatar Apr 10 '24 18:04 holomekc