introproventures / graphql-jpa-query

Generate GraphQL Query Api for your JPA Entity Models
https://github.com/introproventures/graphql-jpa-query
Apache License 2.0
195 stars 54 forks source link

ManyToOne relationships failing with UnknownTableReferenceException #374

Closed clifford-grech closed 1 year ago

clifford-grech commented 1 year ago

Describe the bug @igdianov, since I've updated to the latest release of this library (v1.0.0), which is making use of hibernate-core 6.1.7, when attempting to request a query that triggers a manyToOne join and attempt to filter result with the parent table, it ends up resulting in the following error:

jakarta.persistence.PersistenceException: Converting `org.hibernate.sql.ast.tree.from.UnknownTableReferenceException` to JPA `PersistenceException` : Unable to determine TableReference (`dummy_child`) for `com.acme.dl.data.api.models.dummyChild(597986201461200).dummyParent(597986201487300)`
    at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:165) ~[hibernate-core-6.1.7.Final.jar:6.1.7.Final]
    at org.hibernate.query.spi.AbstractSelectionQuery.list(AbstractSelectionQuery.java:374) ~[hibernate-core-6.1.7.Final.jar:6.1.7.Final]
    at org.hibernate.query.sqm.internal.QuerySqmImpl.list(QuerySqmImpl.java:1073) ~[hibernate-core-6.1.7.Final.jar:6.1.7.Final]
    at org.hibernate.query.Query.getResultList(Query.java:94) ~[hibernate-core-6.1.7.Final.jar:6.1.7.Final]
    at com.introproventures.graphql.jpa.query.schema.impl.GraphQLJpaQueryFactory.getResultList(GraphQLJpaQueryFactory.java:455) ~[graphql-jpa-query-schema-1.0.2-SNAPSHOT.jar:1.0.2-SNAPSHOT]
    at com.introproventures.graphql.jpa.query.schema.impl.GraphQLJpaQueryFactory.loadManyToOne(GraphQLJpaQueryFactory.java:436) ~[graphql-jpa-query-schema-1.0.2-SNAPSHOT.jar:1.0.2-SNAPSHOT]
    at com.introproventures.graphql.jpa.query.schema.impl.GraphQLJpaToOneMappedBatchLoader.lambda$load$0(GraphQLJpaToOneMappedBatchLoader.java:25) ~[graphql-jpa-query-schema-1.0.2-SNAPSHOT.jar:1.0.2-SNAPSHOT]
    at java.base/java.util.concurrent.CompletableFuture$AsyncSupply.run(CompletableFuture.java:1768) ~[na:na]
    at java.base/java.util.concurrent.CompletableFuture$AsyncSupply.exec(CompletableFuture.java:1760) ~[na:na]
    at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:373) ~[na:na]
    at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1182) ~[na:na]
    at java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1655) ~[na:na]
    at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1622) ~[na:na]
    at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:165) ~[na:na]
Caused by: org.hibernate.sql.ast.tree.from.UnknownTableReferenceException: Unable to determine TableReference (`dummy_child`) for `com.acme.dl.data.api.models.dummyChild(597986201461200).dummyParent(597986201487300)`
    at org.hibernate.sql.ast.tree.from.LazyTableGroup.resolveTableReference(LazyTableGroup.java:256) ~[hibernate-core-6.1.7.Final.jar:6.1.7.Final]
    at org.hibernate.query.sqm.sql.internal.EntityValuedPathInterpretation.lambda$from$4(EntityValuedPathInterpretation.java:343) ~[hibernate-core-6.1.7.Final.jar:6.1.7.Final]
    at org.hibernate.metamodel.mapping.internal.SelectableMappingsImpl.forEachSelectable(SelectableMappingsImpl.java:112) ~[hibernate-core-6.1.7.Final.jar:6.1.7.Final]
    at org.hibernate.metamodel.mapping.internal.EmbeddableMappingTypeImpl.forEachSelectable(EmbeddableMappingTypeImpl.java:706) ~[hibernate-core-6.1.7.Final.jar:6.1.7.Final]
    at org.hibernate.metamodel.mapping.internal.EmbeddedAttributeMapping.forEachSelectable(EmbeddedAttributeMapping.java:189) ~[hibernate-core-6.1.7.Final.jar:6.1.7.Final]
    at org.hibernate.metamodel.mapping.ModelPart.forEachSelectable(ModelPart.java:93) ~[hibernate-core-6.1.7.Final.jar:6.1.7.Final]
    at org.hibernate.query.sqm.sql.internal.EntityValuedPathInterpretation.from(EntityValuedPathInterpretation.java:341) ~[hibernate-core-6.1.7.Final.jar:6.1.7.Final]
    at org.hibernate.query.sqm.sql.BaseSqmToSqlAstConverter.visitTableGroup(BaseSqmToSqlAstConverter.java:3642) ~[hibernate-core-6.1.7.Final.jar:6.1.7.Final]
    at org.hibernate.query.sqm.sql.BaseSqmToSqlAstConverter.visitQualifiedAttributeJoin(BaseSqmToSqlAstConverter.java:3440) ~[hibernate-core-6.1.7.Final.jar:6.1.7.Final]
    at org.hibernate.query.sqm.sql.BaseSqmToSqlAstConverter.visitQualifiedAttributeJoin(BaseSqmToSqlAstConverter.java:416) ~[hibernate-core-6.1.7.Final.jar:6.1.7.Final]
    at org.hibernate.query.sqm.SemanticQueryWalker.visitSingularJoin(SemanticQueryWalker.java:209) ~[hibernate-core-6.1.7.Final.jar:6.1.7.Final]
    at org.hibernate.query.sqm.tree.domain.SqmSingularJoin.accept(SqmSingularJoin.java:49) ~[hibernate-core-6.1.7.Final.jar:6.1.7.Final]
    at org.hibernate.query.sqm.sql.BaseSqmToSqlAstConverter.visitSelection(BaseSqmToSqlAstConverter.java:1995) ~[hibernate-core-6.1.7.Final.jar:6.1.7.Final]
    at org.hibernate.query.sqm.sql.BaseSqmToSqlAstConverter.visitSelectClause(BaseSqmToSqlAstConverter.java:1951) ~[hibernate-core-6.1.7.Final.jar:6.1.7.Final]
    at org.hibernate.query.sqm.sql.BaseSqmToSqlAstConverter.visitQuerySpec(BaseSqmToSqlAstConverter.java:1819) ~[hibernate-core-6.1.7.Final.jar:6.1.7.Final]
    at org.hibernate.query.sqm.sql.BaseSqmToSqlAstConverter.visitQuerySpec(BaseSqmToSqlAstConverter.java:416) ~[hibernate-core-6.1.7.Final.jar:6.1.7.Final]
    at org.hibernate.query.sqm.tree.select.SqmQuerySpec.accept(SqmQuerySpec.java:122) ~[hibernate-core-6.1.7.Final.jar:6.1.7.Final]
    at org.hibernate.query.sqm.spi.BaseSemanticQueryWalker.visitQueryPart(BaseSemanticQueryWalker.java:213) ~[hibernate-core-6.1.7.Final.jar:6.1.7.Final]
    at org.hibernate.query.sqm.sql.BaseSqmToSqlAstConverter.visitQueryPart(BaseSqmToSqlAstConverter.java:1679) ~[hibernate-core-6.1.7.Final.jar:6.1.7.Final]
    at org.hibernate.query.sqm.sql.BaseSqmToSqlAstConverter.visitSelectStatement(BaseSqmToSqlAstConverter.java:1477) ~[hibernate-core-6.1.7.Final.jar:6.1.7.Final]
    at org.hibernate.query.sqm.sql.BaseSqmToSqlAstConverter.visitSelectStatement(BaseSqmToSqlAstConverter.java:416) ~[hibernate-core-6.1.7.Final.jar:6.1.7.Final]
    at org.hibernate.query.sqm.tree.select.SqmSelectStatement.accept(SqmSelectStatement.java:213) ~[hibernate-core-6.1.7.Final.jar:6.1.7.Final]
    at org.hibernate.query.sqm.sql.BaseSqmToSqlAstConverter.translate(BaseSqmToSqlAstConverter.java:711) ~[hibernate-core-6.1.7.Final.jar:6.1.7.Final]
    at org.hibernate.query.sqm.internal.ConcreteSqmSelectQueryPlan.buildCacheableSqmInterpretation(ConcreteSqmSelectQueryPlan.java:380) ~[hibernate-core-6.1.7.Final.jar:6.1.7.Final]
    at org.hibernate.query.sqm.internal.ConcreteSqmSelectQueryPlan.withCacheableSqmInterpretation(ConcreteSqmSelectQueryPlan.java:300) ~[hibernate-core-6.1.7.Final.jar:6.1.7.Final]
    at org.hibernate.query.sqm.internal.ConcreteSqmSelectQueryPlan.performList(ConcreteSqmSelectQueryPlan.java:276) ~[hibernate-core-6.1.7.Final.jar:6.1.7.Final]
    at org.hibernate.query.sqm.internal.QuerySqmImpl.doList(QuerySqmImpl.java:571) ~[hibernate-core-6.1.7.Final.jar:6.1.7.Final]
    at org.hibernate.query.spi.AbstractSelectionQuery.list(AbstractSelectionQuery.java:363) ~[hibernate-core-6.1.7.Final.jar:6.1.7.Final]
    ... 12 common frames omitted

To Reproduce Steps to reproduce the behavior:

  1. Create a parent and child model with a bidirectional relationship. Naturally, the relationship in the child model should be a ToOne type. In this case a manyToOne. Also, the model IDs in my case are embedded IDs.
  2. Run a query that triggers the GraphQLJpaQueryFactory.LoadManyToOne(..) method like so:
    query multipleRecordQueryAndJoinToOneWithFilter {
    dummyChilds
    {
    edges{
      node{
        id{col1, col2}
        dummyParent (where:{recordDate : {EQ: "2021-08-04"}}){
          id{colA, colB}
        }
      }
    }
    }
    }

Expected behavior All records in dummyChild which are linked to any record in dummyParent with recordDate = 2021-08-04 should be returned and each record should show both the dummyChild's ID and the dummyParent's ID

Desktop (please complete the following information):

Smartphone (please complete the following information):

Additional context

  1. GraphQLJpaToOneMappedBatchLoader.load(..) gets called, which in turn calls GraphQLJpaQueryFactory.LoadManyToOne(..) <-- This does not happen when we do not filter by the parent joined table.
  2. This leads up to hibernate-core's StandardTableGroup.getTableReferenceInternal(..) not to find any match and returns null, which leads to the UnknownTableReferenceException.

When is it failing? When navigablePath is equal to "com.acme.dl.data.api.models.dummyChild(597986201461200).dummyParent(597986201487300)" and tableExpressoin is "dummy_child"

N.B. - This issue was reported to Hibernate here and resolved with hibernate-core v6.2.0.CR3

clifford-grech commented 1 year ago

Minor update on this issue.

After further troubleshooting, I realized that the problem only happens when I make use of the @NotFound(action = NotFoundAction.IGNORE) annotation in the manyToOne relationship definition.

igdianov commented 1 year ago

@clifford-grech This error is raised due to a broken FK relationship when detected by Hibernate. The best fix is to fix the underlying data inconsistency. Otherwise, you can use ignore action to suppress the problem. This is not a bug in the library code.