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.

Comment From: sp00m

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.

Comment From: harawata

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.

Comment From: harawata

@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!