Mybatis中的TypeHandler有两个功能,一个是完成javaType至jdbcType的转换,另一个是完成jdbcType至javaType的转换。
java
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)apache
注意上面的图的箭头方向,跟着箭头方向读,恰好是javaType to jdbcType和jdbcType to javaType。因此,图并无画错。网络
说TypeHandler的功能原理,并非咱们的重点,由于你们都懂。咱们的重点是,Mybatis如何组织TypeHandler的,以及如何编写一个自定义的TypeHandler,以及TypeHandler是如何“智能”绑定到目标属性的。mybatis
1. TypeHandlerRegistryapp
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。
ide
下面看看,如何注册一个自定义的TypeHandler。ui
<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》。this
注意代码中的那个泛型参数<PhoneNumber>,虽然没有明确指定PhoneTypeHandler做用于哪个property上,Mybatis就是依赖泛型参数<PhoneNumber>,得到泛型参数Class对象,再与反射得到的bean属性Class,进行一一对应的。
spa
(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)。(通过网络爬虫或转载的文章,常常丢失流程图、时序图,格式错乱等,仍是看原版的比较好)