Mybatis3.3.x技術內幕(十二):Mybatis之TypeHandler

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)。(通過網絡爬蟲或轉載的文章,常常丟失流程圖、時序圖,格式錯亂等,仍是看原版的比較好)

相關文章
相關標籤/搜索