Mybatis中TypeHandler的底层实现

TypeHandler的用法

TypeHandler是Mybatis提供的一个用于用户自定义处理数据库类型和java类型之间相互映射的一种工具,它可以实现jdbcType到javaType的自动转换。

要实现自定义的TypeHandler首先需要继承BaseTypeHandler,然后在类上添加@MappedTypes

@MappedTypes({String.class})
public MyTypeHandler extends BaseTypeHandler<T> {
    
}

然后在Mybatis的mapper.xml文件中使用自定义的MyTypeHandler

<insert id="insertUser" useGeneratedKeys="true" keyProperty="id">
        insert into user (username) values (#{username,jdbcType=VARCHAR,javaType=String,typeHandler=MyTypeHandler})
</insert>

TypeHandler的原理

TypeHandler是Mybatis提供的一个接口,具体方法如下

public interface TypeHandler<T> {

  //设置参数
  void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException;

  //取得结果,供普通select用
  T getResult(ResultSet rs, String columnName) throws SQLException;

  //取得结果,供普通select用
  T getResult(ResultSet rs, int columnIndex) throws SQLException;

  //取得结果,供SP用
  T getResult(CallableStatement cs, int columnIndex) throws SQLException;

}

在TypeHandlerRegistry.java文件中有两个属性用来放置Mybatis自带或者是用户自定义的TypeHandler。其中JDBC_TYPE_HANDLER_MAP用来表示JdbcType和TypeHandler的对应关系,而TYPE_HANDLER_MAP则用来表示Java类与JdbcType和TypeHandler的关系
在这里插入图片描述
这两者的区别是一个用来根据JdbcType来获取相应的TypeHandler,一个是根据JavaType和JdbcType来获取相应的TypeHandler,具体的使用方法可以在ResultSetWrapper.java中的getTypeHandler()方法中可以看到
在这里插入图片描述
从第112行开始可以看到一共分为三种情况,分别是

  • javaType存在和jdbcType存在
  • javaType不存在
  • jdbcType不存在
    具体的代码可以到TypeHandlerRegistry类的getTypeHandler()方法中看到,根据是否存在会从两个map中选择一个进行获取

在TypeHandlerRegistry的构造器中可以看到在这里对Mybatis默认实现的一些TypeHandler都会在这里进行注册
在这里插入图片描述
BaseBuilder.java是用来根据用户的xml来建立相应的元素,在BaseBuilder.java中的resolveTypeHandler方法既是为了对用户定义的TypeHandler进行分析并建立相应的映射关系,在这个方法中可以看到,当传入的typeHandlerType还没有被生成过,就对相应javaType对应的typeHandlerType进行初始化操作
在这里插入图片描述
那么resolveTypeHandler()这个方法是在哪里调用的呢,答案就是在SqlSourceBuilder.java的buildParameterMapping()方法中,在这里会对所有用户自定义的元素进行解析,当然也包括定义的TypeHandler
在这里插入图片描述
初始化操作在TypeHandlerRegistry.java中的getInstance()方法中进行,通过反射取得相应TypeHandler的构造器来生成新的自定义TypeHandler对象
在这里插入图片描述

有关TypeHandler是单例还是多例的问题

根据上面的分析可以看到所有的TypeHandler都会被Mybatis保存到一个Map中,每次需要的时候都从中获取,所以从这一点来看毫无疑问TypeHandler是单例的,但是对于如下情况

@MappedTypes({Server.class, Clien.class})
public MyTypeHandler extends BaseTypeHandler<T> {

    Class clazz;
    
    public MyTypeHandler(Class clazz) {
        //构造器
        this.clazz = clazz;
    }
}

当我们在@MappedTypes标签中定义多个class时,在这种情况下,由于Mybatis是以javaType作为Map的键,所以对于多个用户自定义java类型会多次进行对MyTypeHandler的构造,所以会多次进入MyTypeHandler的构造器,如果我们通过将构造器中传入的类型进行保存,则会发现每次传入的clazz的值都是不一样的,Mybatis会使用遍历的方法把每一个注解中的类型都传入一次进行构造,从这一点来看表现则像是多例的,我们可以得到很多clazz属性不同的MyTypeHandler类,但是每一种仍然只会生成一次,原因如上所说,所以我们可以利用这一点将传入的clazz保存起来实现一个通用的自定义TypeHandler来对一些类型进行整合,比如使用TypeHandler进行JSON对象和Java对象的转换,而不用为每一个Java对象都实现一遍自定义的TypeHandler。

猜你喜欢

转载自blog.csdn.net/turbo_zone/article/details/83795018