文档章节

Mybatis3.3.x技术内幕(十二):Mybatis之TypeHandler

祖大俊
 祖大俊
发布于 2016/05/06 21:03
字数 990
阅读 1661
收藏 14

Mybatis中的TypeHandler有两个功能,一个是完成javaType至jdbcType的转换,另外一个是完成jdbcType至javaType的转换。

public interface TypeHandler<T> {
  void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException;
  T getResult(ResultSet rs, String columnName) throws SQLException;
  T getResult(ResultSet rs, int columnIndex) throws SQLException;
  T getResult(CallableStatement cs, int columnIndex) throws SQLException;
}

上面的接口方法,归结起来,其实就是两个方法:setParameter()和getResult()。

(Made In Edrawmax)

注意上面的图的箭头方向,跟着箭头方向读,刚好是javaType to jdbcType和jdbcType to javaType。所以,图并没有画错。

说TypeHandler的功能原理,并不是我们的重点,因为大家都懂。我们的重点是,Mybatis如何组织TypeHandler的,以及如何编写一个自定义的TypeHandler,以及TypeHandler是如何“智能”绑定到目标属性的。

1. TypeHandlerRegistry

public final class TypeHandlerRegistry {
  // EnumMap,保存Mybatis内部提供的枚举JdbcType类型和对应的TypeHandler
  private final Map<JdbcType, TypeHandler<?>> JDBC_TYPE_HANDLER_MAP = new EnumMap<JdbcType, TypeHandler<?>>(JdbcType.class);
  
  // Type:javaType的Class类型(Type是Class的接口),value是一个Map集合(比如String,可能对应数据库的clob、char、varchar等,所以是一对多关系)
  private final Map<Type, Map<JdbcType, TypeHandler<?>>> TYPE_HANDLER_MAP = new HashMap<Type, Map<JdbcType, TypeHandler<?>>>();
  
  // 处理Object类型(运行时,会尝试进行向下类型转换找到合适的TypeHandler,如果依然失败,最后选择ObjectTypeHandler)
  private final TypeHandler<Object> UNKNOWN_TYPE_HANDLER = new UnknownTypeHandler(this);
  
  // 所有的TypeHandler. Key:TypeHandler的Class类型,value:TypeHandler实例(都是singleton)
  private final Map<Class<?>, TypeHandler<?>> ALL_TYPE_HANDLERS_MAP = new HashMap<Class<?>, TypeHandler<?>>();

  public TypeHandlerRegistry() {
    register(Boolean.class, new BooleanTypeHandler());
    register(boolean.class, new BooleanTypeHandler());
    register(JdbcType.BOOLEAN, new BooleanTypeHandler());
    register(JdbcType.BIT, new BooleanTypeHandler());

    register(Byte.class, new ByteTypeHandler());
    register(byte.class, new ByteTypeHandler());
    register(JdbcType.TINYINT, new ByteTypeHandler());
    // ...

Mybatis主要使用Map<Type, Map<JdbcType, TypeHandler<?>>> TYPE_HANDLER_MAP,来获取TypeHandler。

下面看看,如何注册一个自定义的TypeHandler。

<typeHandlers>
	<typeHandler handler="com.mybatis3.typehandlers.PhoneTypeHandler" />
</typeHandlers>
public class PhoneTypeHandler extends BaseTypeHandler<PhoneNumber> {
	@Override
	public void setNonNullParameter(PreparedStatement ps, int i, PhoneNumber parameter, JdbcType jdbcType)
			throws SQLException {
		ps.setString(i, parameter.getAsString());
	}

	@Override
	public PhoneNumber getNullableResult(ResultSet rs, String columnName) throws SQLException {
		return new PhoneNumber(rs.getString(columnName));
	}
	// ...

以上例子,来自于《Java Persistence with MyBatis 3》。

注意代码中的那个泛型参数<PhoneNumber>,虽然没有明确指定PhoneTypeHandler作用于哪一个property上,Mybatis就是依赖泛型参数<PhoneNumber>,获得泛型参数Class对象,再与反射获得的bean属性Class,进行一一对应的。

(Made In Edrawmax)

告诉大家一个秘密,在Spring MVC中,其Converter<S, T>,就是使用上面的泛型参数与反射原理,从一堆转换器中,准确找到那一个转换器的。

org.apache.ibatis.type.TypeHandlerRegistry.register(TypeHandler<T>)方法源码。

register(typeReference.getRawType(), typeHandler);

上面的getRawType(),就是泛型参数Type类型。Mybatis在启动初始化过程中,会将用户自定义的<typeHandlers>标签内的所有TypeHandler,注册至Configuration内。


除了上面的“智能”绑定外,我们还可以手动绑定TypeHandler。

<result property="phone" column="phone" typeHandler="com.mybatis3.typehandlers.PhoneTypeHandler"/>

手动绑定的TypeHandler优先级较高。


2. 给每一个属性绑定TypeHandler

属性封装,具体对应Mybatis中的ResultMapping或ParameterMapping封装,必须给每一个属性绑定一个TypeHandler。

org.apache.ibatis.mapping.ResultMapping.Builder.resolveTypeHandler()方法源码。ParameterMapping也是类似的,不再重复了。

private void resolveTypeHandler() {
      if (resultMapping.typeHandler == null && resultMapping.javaType != null) {
        Configuration configuration = resultMapping.configuration;
        TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();
        resultMapping.typeHandler = typeHandlerRegistry.getTypeHandler(resultMapping.javaType, resultMapping.jdbcType);
      }
    }


3. TypeHandler的使用

org.apache.ibatis.scripting.defaults.DefaultParameterHandler.setParameters()方法源码。

TypeHandler typeHandler = parameterMapping.getTypeHandler();
typeHandler.setParameter(ps, i + 1, value, jdbcType);

通过TypeHandler转换设置参数。

org.apache.ibatis.executor.resultset.DefaultResultSetHandler.createPrimitiveResultObject()方法源码。

final TypeHandler<?> typeHandler = rsw.getTypeHandler(resultType, columnName);
return typeHandler.getResult(rsw.getResultSet(), columnName);

通过TypeHandler转换获取结果。

以上便是Mybatis中,有关TypeHandler的内容,一般情况下,我们不需要自定义TypeHandler,Mybatis内置了大多数常见的TypeHandler。只有Mybatis不能满足复杂类型转换时,我们才考虑自定义。


版权提示:文章出自开源中国社区,若对文章感兴趣,可关注我的开源中国社区博客(http://my.oschina.net/zudajun)。(经过网络爬虫或转载的文章,经常丢失流程图、时序图,格式错乱等,还是看原版的比较好)

© 著作权归作者所有

共有 人打赏支持
祖大俊
粉丝 748
博文 32
码字总数 52477
作品 0
昌平
私信 提问
Mybatis3.3.x技术内幕(十):Mybatis初始化流程(下)

Mybatis初始化过程中,解析parameterMap、resultMap、"select|insert|update|delete"元素,无疑是重头戏。本节将详细分析解析过程。 元素parameterMap将会解析为ParameterMap对象,该对象包含...

祖大俊
2016/05/04
851
0
Mybatis3.3.x技术内幕(一):SqlSession和SqlSessionFactory列传

前言:我长大了,成年了,有需求,但我单身,所以我要讨个媳妇,要求是:漂亮、高挑、身材好、笑容甜美…… 和A相亲:漂亮,不够高挑。 和B相亲:高挑,身材不够好。 和C相亲:身材好,笑容不...

祖大俊
2016/04/25
3.3K
2
Mybatis3.3.x技术内幕(八):Mybatis初始化流程(上)

Mybatis初始化流程,其实就是组装重量级All-In-One对象Configuration的过程,主要分为系统环境参数初始化和Mapper映射初始化,其中Mapper映射初始化尤为重要。 inputStream = Resources.getR...

祖大俊
2016/05/02
1K
2
mybatis(五)TypeHandler简介及配置(mybatis源码篇)

上篇文章为大家介绍了mybatis中别名的使用,以及其源码。本篇将为大家介绍TypeHandler, 并简单分析其源码。 Mybatis中的TypeHandler是什么?   无论是 MyBatis 在预处理语句(PreparedSta...

LCZ777
2014/12/19
0
0
Mybatis 类型转换源码分析

本文将从以下几个方面进行介绍 相关文章 前言 类型处理器 类型注册器 别名注册器 相关文章 Mybatis 解析配置文件的源码解析 前言 JDBC 提供的数据类型和Java的数据类型并不是完全对应的,当 ...

tsmyk0715
2018/11/04
0
0

没有更多内容

加载失败,请刷新页面

加载更多

如果让你写一个消息队列,该如何进行架构设计?

面试题 如果让你写一个消息队列,该如何进行架构设计?说一下你的思路。 面试官心理分析 其实聊到这个问题,一般面试官要考察两块: 你有没有对某一个消息队列做过较为深入的原理的了解,或者...

李红欧巴
今天
4
0
错题

无知的小狼
今天
2
0
PowerShell因为在此系统中禁止执行脚本的解决方法

参考:window系统包管理工具--chocolatey 报错提示: & : 无法加载文件 C:\Users\liuzidong\AppData\Local\Temp\chocolatey\chocInstall\tools\chocolateyInstall.ps1,因为在此系统上禁止运...

近在咫尺远在天涯
今天
3
0
TP5 跨域请求处理

https://blog.csdn.net/a593706205/article/details/81774987 https://blog.csdn.net/wyk9916/article/details/82315700...

15834278076
今天
3
0
深入理解java虚拟机-Java内存区域与内存溢出异常

深入理解java虚拟机 Java内存区域与内存溢出异常 运行时数据区域 程序计数器 线程私有,内存小,是当前线程执行的字节码行号指示器,字节码解释器通过改变这个计数器的值来选取下一条需要执行...

须臾之余
今天
5
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部