MyBatis version

mybatis:3.4.6 mybatis-spring-boot-starter:1.3.2

Database vendor and version

mysql:5.7

Test case or example project

git: https://gitee.com/jervain_y/demo-mybatis.git

Steps to reproduce

run testcase

Expected result

when we use @Param annotation, like this param.field, that cause my custom TypeHandler dosen't work

Actual result

this is run log

2019-01-15 14:15:05.393 DEBUG 6460 --- [           main] com.example.demo2.AMapper.updateMyOne    : ==>  Preparing: update a set demo=? where id = ? 
2019-01-15 14:15:05.417 DEBUG 6460 --- [           main] com.example.demo2.AMapper.updateMyOne    : ==> Parameters: [](ArrayList), 1(Integer)

expect Parameters: [](String), 1(Integer)

Comment From: kazuki43zoo

@jervainy

In this case, the MyBatis uses the UnknownTypeHandler. It delegate to a TypeHandler that associate with actual value type(e.g. ArrayList). Therefore, you can resolve this issue by specify the @MappedTypes on the ListTypeHandler as follow:

@MappedTypes({List.class, ArrayList.class})
public class ListTypeHandler extends BaseTypeHandler<List> {
  // ...
}

Please try above solution.

Comment From: jervainy

@kazuki43zoo List is the parent class of ArrayList. Why do I need to declare ArrayList in MappedTypes? Sometimes I define a List or Map variable. I don't know the specific type of it.

Comment From: kazuki43zoo

@jervainy

Why do I need to declare ArrayList in MappedTypes?

In current implementation, the MyBatis does not support for searching a TypeHandler that associate with interface (NOTE: Can be search a TypeHander that associate with super class) when use together with @Param.

@harawata Is better consider to support for searching a TypeHandler that associate with interface?

Comment From: harawata

I don't think so. The posted type handler would not support other List implementations like LinkedList (note that ObjectMapper creates an ArrayList). TypeHandler has to know the concrete type to instantiate a result.

Comment From: jervainy

Most of the time we declare variables based on interfaces. We can't accurately determine their specific type during the run. A List may be ArrayList, LinkList or a list we define. If we declare all the variables in MappedType, the code will not be so elegant.

Comment From: harawata

I think I misunderstood @kazuki43zoo 's comment. Let me check.

Comment From: harawata

Hi all,

I'm sorry for a late reply. My conclusion is that the problem (it seems more like a limitation rather than a bug) is in the property type resolution during the parsing phase, and type handler resolution works as expected.

To map type handler, MyBatis has to know the type of the property. For a parameter, MyBatis first tries to resolve the property type in the parsing phase. This resolution, however, is not very thorough and the property type is not correctly resolved in some cases. It is usually not a problem because type resolution is performed again using the actual parameter instance. In this case, however, the actual property type is different (e.g. ArrayList, etc.) and the ListTypeHandler which is registered against List is not returned.

This would explain why adding ArrayList to @MappedType resolves the problem. And specifying type handler explicitly in the parameter expression should work as well (e.g. #{a.demo,typeHandler=....ListTypeHandler}).

The test in a portable form is here, but I won't be able to work on it anytime soon (it's a corner case with workarounds, so the priority is low).