spring-projects / spring-graphql

Spring Integration for GraphQL
https://spring.io/projects/spring-graphql
Apache License 2.0
1.53k stars 305 forks source link

`QuerydslDataFetcher.buildPredicate(…)` does not unwrap nested argument maps into fully-qualified property paths #1081

Open zeng-alt opened 1 week ago

zeng-alt commented 1 week ago

There is a problem with the QuerydsDataFetcher join query. Can I modify the QuerydsDataFetcher.getArgumentValues ​​method to fix it?

image /**

mp911de commented 5 days ago

If you would like us to spend some time helping you to diagnose the problem, please spend some time describing it and, ideally, providing a minimal yet complete sample that reproduces the problem. You can share it with us by pushing it to a separate repository on GitHub or by zipping it up and attaching it to this issue.

zeng-alt commented 5 days ago

This is my example repository: https://github.com/zeng-alt/graphql-test

This is my solution, but it's not very good:https://github.com/zeng-alt/zero-ddd/tree/master/zero-ddd-component/zero-ddd-graphql-component

mp911de commented 4 days ago

This is indeed a bug on our side. We're storing incoming arguments in a map expecting keys of the map to represent full property paths. In this case (userInput: { name: "add", address: { country: "12" } }), the key address contains a nested map ({country=12}) that we're trying to bind to the address property.

What should happen is that we unwrap all nested maps and form an argument map with fully-qualified keys (address.country=12 instead of address={country=12}).

Stack trace:

org.springframework.dao.InvalidDataAccessApiUsageException: Argument [{country=12}] of type [java.util.LinkedHashMap] did not match parameter type [com.zjj.graphqltest.domain.Address (n/a)]
    at org.springframework.orm.jpa.EntityManagerFactoryUtils.convertJpaAccessExceptionIfPossible(EntityManagerFactoryUtils.java:371) ~[spring-orm-6.1.14.jar:6.1.14]
    at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:246) ~[spring-orm-6.1.14.jar:6.1.14]
    at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.translateExceptionIfPossible(AbstractEntityManagerFactoryBean.java:550) ~[spring-orm-6.1.14.jar:6.1.14]
    at org.springframework.dao.support.ChainedPersistenceExceptionTranslator.translateExceptionIfPossible(ChainedPersistenceExceptionTranslator.java:61) ~[spring-tx-6.1.14.jar:6.1.14]
    at org.springframework.dao.support.DataAccessUtils.translateIfNecessary(DataAccessUtils.java:335) ~[spring-tx-6.1.14.jar:6.1.14]
    at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:160) ~[spring-tx-6.1.14.jar:6.1.14]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184) ~[spring-aop-6.1.14.jar:6.1.14]
    at org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulatingMethodInterceptor.invoke(CrudMethodMetadataPostProcessor.java:165) ~[spring-data-jpa-3.3.5.jar:3.3.5]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184) ~[spring-aop-6.1.14.jar:6.1.14]
    at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:97) ~[spring-aop-6.1.14.jar:6.1.14]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184) ~[spring-aop-6.1.14.jar:6.1.14]
    at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:223) ~[spring-aop-6.1.14.jar:6.1.14]
    at jdk.proxy2/jdk.proxy2.$Proxy113.findBy(Unknown Source) ~[na:na]
    at org.springframework.graphql.data.query.QuerydslDataFetcher$ManyEntityFetcher.get(QuerydslDataFetcher.java:842) ~[spring-graphql-1.3.3.jar:1.3.3]
    at org.springframework.graphql.data.query.QuerydslDataFetcher$ManyEntityFetcher.get(QuerydslDataFetcher.java:814) ~[spring-graphql-1.3.3.jar:1.3.3]
    at org.springframework.graphql.execution.ContextDataFetcherDecorator.lambda$get$0(ContextDataFetcherDecorator.java:87) ~[spring-graphql-1.3.3.jar:1.3.3]
    at io.micrometer.context.ContextSnapshot.lambda$wrap$1(ContextSnapshot.java:106) ~[context-propagation-1.1.2.jar:1.1.2]
    at org.springframework.graphql.execution.ContextDataFetcherDecorator.get(ContextDataFetcherDecorator.java:87) ~[spring-graphql-1.3.3.jar:1.3.3]
    at graphql.execution.ExecutionStrategy.invokeDataFetcher(ExecutionStrategy.java:533) ~[graphql-java-22.3.jar:na]
    at graphql.execution.ExecutionStrategy.fetchField(ExecutionStrategy.java:497) ~[graphql-java-22.3.jar:na]
…
Caused by: org.hibernate.query.QueryArgumentException: Argument [{country=12}] of type [java.util.LinkedHashMap] did not match parameter type [com.zjj.graphqltest.domain.Address (n/a)]
    at org.hibernate.query.spi.QueryParameterBindingValidator.validate(QueryParameterBindingValidator.java:85) ~[hibernate-core-6.5.3.Final.jar:6.5.3.Final]
    at org.hibernate.query.spi.QueryParameterBindingValidator.validate(QueryParameterBindingValidator.java:32) ~[hibernate-core-6.5.3.Final.jar:6.5.3.Final]
    at org.hibernate.query.internal.QueryParameterBindingImpl.validate(QueryParameterBindingImpl.java:362) ~[hibernate-core-6.5.3.Final.jar:6.5.3.Final]
    at org.hibernate.query.internal.QueryParameterBindingImpl.setBindValue(QueryParameterBindingImpl.java:137) ~[hibernate-core-6.5.3.Final.jar:6.5.3.Final]
    at org.hibernate.query.spi.AbstractCommonQueryContract.setParameter(AbstractCommonQueryContract.java:933) ~[hibernate-core-6.5.3.Final.jar:6.5.3.Final]
    at org.hibernate.query.spi.AbstractSelectionQuery.setParameter(AbstractSelectionQuery.java:906) ~[hibernate-core-6.5.3.Final.jar:6.5.3.Final]
    at org.hibernate.query.sqm.internal.QuerySqmImpl.setParameter(QuerySqmImpl.java:1229) ~[hibernate-core-6.5.3.Final.jar:6.5.3.Final]
    at org.hibernate.query.sqm.internal.QuerySqmImpl.setParameter(QuerySqmImpl.java:136) ~[hibernate-core-6.5.3.Final.jar:6.5.3.Final]
    at com.querydsl.jpa.impl.JPAUtil.setConstants(JPAUtil.java:61) ~[querydsl-jpa-5.0.0-jakarta.jar:na]
    at com.querydsl.jpa.impl.AbstractJPAQuery.createQuery(AbstractJPAQuery.java:133) ~[querydsl-jpa-5.0.0-jakarta.jar:na]
    at com.querydsl.jpa.impl.AbstractJPAQuery.createQuery(AbstractJPAQuery.java:125) ~[querydsl-jpa-5.0.0-jakarta.jar:na]
    at com.querydsl.jpa.impl.AbstractJPAQuery.fetch(AbstractJPAQuery.java:242) ~[querydsl-jpa-5.0.0-jakarta.jar:na]
    at org.springframework.data.jpa.repository.support.FetchableFluentQueryByPredicate.all(FetchableFluentQueryByPredicate.java:158) ~[spring-data-jpa-3.3.5.jar:3.3.5]
    at org.springframework.graphql.data.query.QuerydslDataFetcher$ManyEntityFetcher.getResult(QuerydslDataFetcher.java:861) ~[spring-graphql-1.3.3.jar:1.3.3]
    at org.springframework.graphql.data.query.QuerydslDataFetcher$ManyEntityFetcher.lambda$get$0(QuerydslDataFetcher.java:856) ~[spring-graphql-1.3.3.jar:1.3.3]
    at org.springframework.data.jpa.repository.support.QuerydslJpaPredicateExecutor.findBy(QuerydslJpaPredicateExecutor.java:242) ~[spring-data-jpa-3.3.5.jar:3.3.5]
…