Skip to content

When using @ConstructorArgs, shouldn't implicit type resolution be run against the constructor arguments? #2787

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
sp00m opened this issue Feb 1, 2023 · 3 comments

Comments

@sp00m
Copy link

sp00m commented Feb 1, 2023

MyBatis version

3.5.11

Steps to reproduce

class MyType {
    final String computed;
    public MyType(Integer i) {
        this.computed = i + "";
    }
}
@Select("...")
@ConstructorArgs({ @Arg(column = "...", name = "i") })
List<MyType> select();

Expected result

MyBatis should understand that i is expected to be of type Integer given it's the type of the matching argument in the constructor.

Actual result

I get in the logs:

Found a constructor with arg names [i], but the type of 'i' did not match. Specified: [java.lang.Object] Declared: [java.lang.Integer]

I believe this is due to the fact that MapperAnnotationBuilder#applyConstructorArgs calls MapperBuilderAssistant#buildResultMapping, which tries to resolve the Java type by looking at the class' fields, instead of by looking at the constructor args as one could think given @ConstructorArgs is used.

In my example, because MyType doesn't have a field named i, only a constructor arg, then it doesn't successfully resolve the type of i and defaults to Object (see MapperBuilderAssistant#resolveResultJavaType).

I believe when @ConstructorArgs is used, then implicit type resolution should be run against the constructor arguments.

Workaround

Explicitly specify the Java type to use:

@Arg(column = "i", name = "i", javaType = Integer.class)

Side notes

Relates to #2207, although not exactly the same case I believe.

@sp00m
Copy link
Author

sp00m commented Feb 1, 2023

Just FYI, another perhaps related issue: @MappedTypes on top of a TypeHandler isn't taken into account when @ConstructorArgs is used without explicitly specifying the Java types. I'm not 100% sure whether it should though.

In a nutshell, building upon my above example:

@MappedTypes({ Integer.class })
class CustomIntegerHandler implements TypeHandler<Integer> {  }
@ConstructorArgs({ @Arg(column = "...", name = "i", typeHandler = CustomIntegerHandler.class ) })

This fails too with the same message as above, because the specified typeHandler isn't used when trying to resolve the type of i, which I believe it should.

Let me know if you believe this deserves its own ticket.

@harawata
Copy link
Member

harawata commented Feb 1, 2023

Hello @sp00m ,

Constructors are identified by parameter types, not by argument names.
You can omit the javaType only when there is a writable property with the same name and type.
It's explained in the documentation.

@harawata
Copy link
Member

harawata commented Feb 10, 2023

@sp00m ,

I have added the following statement to the error message.

Note that 'javaType' is required when there is no readable property with the same name ('name' is optional, BTW).

For constructor mappings, name attribute is optional and is useful only when it's difficult to synchronize the argument order. See #721 .

Assuming that there is no further action needed, I'm going to close this.
Thanks again for taking the time to submit the report!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants