Skip to content

Support for both constructor and setter argument binding #1163

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
desiderati opened this issue Mar 19, 2025 · 1 comment
Closed

Support for both constructor and setter argument binding #1163

desiderati opened this issue Mar 19, 2025 · 1 comment
Assignees
Labels
in: data Issues related to working with data type: enhancement A general enhancement
Milestone

Comments

@desiderati
Copy link

If you have defined a class with some properties in the constructor and other properties being accessed via setter methods, only one of these (either the constructor or the setters) will be mapped.

data class TestObject(
    val name: String
) {
    var cellphone: String = ""
}

@Controller
class TestResolver {
    @MutationMapping
    fun createTestObject(
        @Argument testObject: TestObject
    ) {
        println(testObject.cellphone) // Will be empty
    }
}
input TestObjectInput {
    name: String!
    cellphone: String!
}

type Mutation {
    createTestObject(testObject: TestObjectInput!)
}

schema {
    mutation: Mutation
}

mutation createTestObjectWithoutValidation($testObject: TestObjectInput!) {
    createTestObjectWithoutValidation(testObject: $testObject)
}

{
  "testObject": {
    "name": "Felipe",
    "cellphone": "(41) 98765-4321"
  }
}

The issue arises because, in line 260 of the GraphQlArgumentBinder class, if the type object has a constructor with at least one argument, the binder uses that constructor to map the properties. If not, it defaults to using the setter methods to populate them. The idea with Kotlin is that you can create a data class with a primary constructor, where properties are used in equals() and hashCode(), but other properties, like the 'cellphone' property mentioned above, may not be. So, why not support both approaches, populate them via a constructor and setters?

GraphQlArgumentBinder.java
...
Constructor<?> constructor = BeanUtils.getResolvableConstructor(targetClass);

Object value = (constructor.getParameterCount() > 0) ?
    bindMapToObjectViaConstructor(rawMap, constructor, targetType, bindingResult) :
    bindMapToObjectViaSetters(rawMap, constructor, targetType, bindingResult);
...

Version: Spring GraphQL 1.3.1

Thanks, Felipe
@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged label Mar 19, 2025
@dgerhardt
Copy link

dgerhardt commented Apr 7, 2025

I also ran into this issue. There currently does not seem to be a way to use Spring for GraphQL with Kotlin data classes. I tried to define a secondary constructor without parameters but it is not used:

data class SomeEntity(var id: UUID?, [...]) {
  constructor() : this(null, [...])
}

Stacktrace:

java.lang.IllegalStateException: Invalid number of parameter names: 8 for constructor public com.example.SomeEntity(java.util.UUID,[...],kotlin.jvm.internal.DefaultConstructorMarker)
        at org.springframework.util.Assert.state(Assert.java:101) ~[spring-core-6.2.5.jar:6.2.5]
        at org.springframework.beans.BeanUtils.getParameterNames(BeanUtils.java:672) ~[spring-beans-6.2.5.jar:6.2.5]
        at org.springframework.graphql.data.GraphQlArgumentBinder.bindMapToObjectViaConstructor(GraphQlArgumentBinder.java:295) ~[spring-graphql-1.3.4.jar:1.3.4]
        at org.springframework.graphql.data.GraphQlArgumentBinder.bindMap(GraphQlArgumentBinder.java:261) ~[spring-graphql-1.3.4.jar:1.3.4]
        at org.springframework.graphql.data.GraphQlArgumentBinder.bindRawValue(GraphQlArgumentBinder.java:207) ~[spring-graphql-1.3.4.jar:1.3.4]
        at org.springframework.graphql.data.GraphQlArgumentBinder.bind(GraphQlArgumentBinder.java:161) ~[spring-graphql-1.3.4.jar:1.3.4]
        at org.springframework.graphql.data.GraphQlArgumentBinder.bind(GraphQlArgumentBinder.java:145) ~[spring-graphql-1.3.4.jar:1.3.4]
        at org.springframework.graphql.data.query.QueryByExampleDataFetcher.buildExample(QueryByExampleDataFetcher.java:135) ~[spring-graphql-1.3.4.jar:1.3.4]

Edit: After further testing, I don't believe my issue is related to this. It is caused by the use of value classes in my case, so I've opened a separate issue #1186.

@bclozel bclozel added type: enhancement A general enhancement in: data Issues related to working with data and removed status: waiting-for-triage An issue we've not yet triaged labels Apr 15, 2025
@bclozel bclozel added this to the 1.4.0-RC1 milestone Apr 15, 2025
@bclozel bclozel self-assigned this Apr 15, 2025
@rstoyanchev rstoyanchev changed the title Incorrect binding of the mapped object with a constructor that contains properties and defined setters Support for both constructor and setter argument binding Apr 17, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in: data Issues related to working with data type: enhancement A general enhancement
Projects
None yet
Development

No branches or pull requests

4 participants