Skip to content

TypeHandler lost information of actualTypeArguments when class is ParameterizedType #2187

Closed
@DavidTangWei

Description

@DavidTangWei

MyBatis version

3.4.5

Database vendor and version

mysql 5.7+

Test case or example project

deserialize data in table test to POJO Test using mybatis mapper.xml.

create table test {
    `id` bigint pk,
    `names` json
}
//POJO Test    
class Test {
    private long id;
    public void setId(long id) {
        this.id = id;
    }
    private List<String> names;
    public void setNames(List<String> names) {
        this.names = names;
    }
}

// typehandler use jackson for deserialization 
// the clazz in constructor lost actualTypeArguments when real class is ParameterizedType
class JsonTypeHandler<T>(private var clazz: Class<T>) : BaseTypeHandler<T>() {
    
    protected val objectMapper = ObjectMapper()
   
    override fun setNonNullParameter(ps: PreparedStatement?, i: Int, parameter: T, jdbcType: JdbcType?) {
        ps?.setString(i, this.toJson(parameter))
    }

    private fun toJson(obj: T): String {
        try {
            return this.objectMapper.writeValueAsString(obj)
        } catch (e: Exception) {
            throw RuntimeException(e);
        }
    }
    <resultMap id="testMapper" type="Test">
        <result property="id" column="id" />
        <result property="names" column="names" 
                typeHandler="JsonTypeHandler"/>
    </resultMap>

After debug the source code of mybatis, the class in the constructor of TypeHandler is get from org.apache.ibatis.reflection.Reflector#setTypes. But when the type is ParameterizedType to be added, ignore the actualTypeArguments and only add rawType.

//org.apache.ibatis.reflection.Reflector
 private void addSetMethod(String name, Method method) {
   if (isValidPropertyName(name)) {
     setMethods.put(name, new MethodInvoker(method));
     Type[] paramTypes = TypeParameterResolver.resolveParamTypes(method, type);
     setTypes.put(name, typeToClass(paramTypes[0]));
   }
 }

 private Class<?> typeToClass(Type src) {
   Class<?> result = null;
   if (src instanceof Class) {
     result = (Class<?>) src;
   } else if (src instanceof ParameterizedType) {
     // ignore the `actualTypeArguments` and only add `rawType`.
     result = (Class<?>) ((ParameterizedType) src).getRawType();
   } 
   ...
   }
   return result;
 }

Expected result

TypeHandler need to get the real ParameterizedType. Otherwise, I need to write many useless code to create many class inherit TypeHandler to pass the ParameterizedType manual.

Actual result

TypeHandler lost information of actualTypeArguments when class is ParameterizedType

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions