2.3、mybatis原始碼分析-基礎模組之型別轉換
- java資料型別和JDBC資料型別不是完全對等,在執行sql引數繫結和結果集處理時候需要進行Java和JDBC之間的型別轉換。
- 在Mybatis中使用類處理器來完成上述的兩種轉換。
一、TypeHandler
在myabtis中一般情況下TypeHandler用於完成單個引數以及單個列值得型別轉換,在mybatis中所有的型別轉換器都繼承了TypeHandler介面。
1、TypeHandler介面
public interface TypeHandler<T> {
//負責將資料由JdbcType轉為Java型別
void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException;
//將Java轉為JdbcType型別
T getResult(ResultSet rs, String columnName) throws SQLException;
T getResult(ResultSet rs, int columnIndex) throws SQLException;
T getResult(CallableStatement cs, int columnIndex) throws SQLException;
}
2、BaseTypeHandler實現該介面並繼承TypeRefrence抽象類
- 來看BaseTypeHandler具體實現了型別轉換的兩個方法
1.負責將資料由JdbcType轉為Java型別
public void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException {
if (parameter == null) {
if (jdbcType == null) {
throw new TypeException("JDBC requires that the JdbcType must be specified for all nullable parameters.");
}
try {
//繫結引數為null的處理
ps.setNull(i, jdbcType.TYPE_CODE);
} catch (SQLException e) {
throw new TypeException("Error setting null for parameter #" + i + " with JdbcType " + jdbcType + " . " +
"Try setting a different JdbcType for this parameter or a different jdbcTypeForNull configuration property. " +
"Cause: " + e, e);
}
} else {
//繫結非空引數,該方法為抽象方法,由子類實現
setNonNullParameter(ps, i, parameter, jdbcType);
}
}
2.將Java轉為JdbcType型別
public T getResult(ResultSet rs, String columnName) throws SQLException {
//抽象方法,由子類實現
T result = getNullableResult(rs, columnName);
if (rs.wasNull()) {
return null;
} else {
return result;
}
}
3、BaseTypeHandler子類
- 子類很多,大多數是直接呼叫PrepareStatement和ResultSet或者CallableStatement的對應方法
- 以IntegerTypeHandler為例子介紹
@Override
public void setNonNullParameter(PreparedStatement ps, int i, Integer
parameter, JdbcType jdbcType)
throws SQLException {
//實現引數繫結
ps.setInt(i, parameter);
}
@Override
public Integer getNullableResult(ResultSet rs, String columnName)
throws SQLException {
//根據結果集獲取資料
return rs.getInt(columnName);
}
二、TypeHandlerRegistry
- 用於管理TypeHandler和實現TypeHandler註冊
1、屬性及建構函式
1、重點屬性
//記錄jdbcType和TypeHandler之間的對應關係,用於從資料集讀取資料時候將資料從jdbc類轉為java型別
private final Map<JdbcType, TypeHandler<?>> JDBC_TYPE_HANDLER_MAP = new EnumMap<JdbcType, TypeHandler<?>>(JdbcType.class);
//記錄java型別向jdbcType轉換,需要使用到TypeHandler
private final Map<Type, Map<JdbcType, TypeHandler<?>>> TYPE_HANDLER_MAP = new HashMap<Type, Map<JdbcType, TypeHandler<?>>>();
//記錄了全部TypeHandler的型別以及該型別相應的TypeHandler物件
private final TypeHandler<Object> UNKNOWN_TYPE_HANDLER = new UnknownTypeHandler(this);
//空TypeHandler集合的標識
private final Map<Class<?>, TypeHandler<?>> ALL_TYPE_HANDLERS_MAP = new HashMap<Class<?>, TypeHandler<?>>();
2、建構函式
//建構函式中已經完成了很多型別的註冊
public TypeHandlerRegistry() {
register(Boolean.class, new BooleanTypeHandler());
register(boolean.class, new BooleanTypeHandler());
register(JdbcType.BOOLEAN, new BooleanTypeHandler());
register(JdbcType.BIT, new BooleanTypeHandler());
..........
}
2、TypeHandler的註冊
有很多的register的過載方法,大多數會最終會呼叫下面的方法完成註冊
private void register(Type javaType, JdbcType jdbcType, TypeHandler<?> handler) {
if (javaType != null) {
Map<JdbcType, TypeHandler<?>> map = TYPE_HANDLER_MAP.get(javaType);
if (map == null || map == NULL_TYPE_HANDLER_MAP) {
map = new HashMap<JdbcType, TypeHandler<?>>();
TYPE_HANDLER_MAP.put(javaType, map);
}
map.put(jdbcType, handler);
}
ALL_TYPE_HANDLERS_MAP.put(handler.getClass(), handler);
}
3、查詢TypeHandler
private <T> TypeHandler<T> getTypeHandler(Type type, JdbcType jdbcType) {
if (ParamMap.class.equals(type)) {
return null;
}
//獲取JdbcType、TypeHandler對映關係的map
Map<JdbcType, TypeHandler<?>> jdbcHandlerMap = getJdbcHandlerMap(type);
TypeHandler<?> handler = null;
if (jdbcHandlerMap != null) {
handler = jdbcHandlerMap.get(jdbcType);
if (handler == null) {
handler = jdbcHandlerMap.get(null);
}
if (handler == null) {
// 如果只是註冊了一個Typehandler則使用此Typehandler物件
handler = pickSoleHandler(jdbcHandlerMap);
}
}
return (TypeHandler<T>) handler;
}
---------------------------------------------------------------
1.獲取JdbcType、TypeHandler對映關係的map
private Map<JdbcType, TypeHandler<?>> getJdbcHandlerMap(Type type) {
//查詢java對應的TypeHandler集合
Map<JdbcType, TypeHandler<?>> jdbcHandlerMap = TYPE_HANDLER_MAP.get(type);
if (NULL_TYPE_HANDLER_MAP.equals(jdbcHandlerMap)) {
return null;
}
if (jdbcHandlerMap == null && type instanceof Class) {
Class<?> clazz = (Class<?>) type;
if (clazz.isEnum()) {
//如果是列舉型別,查詢父介面對應的TypeHandler集合,作為初始化集合
jdbcHandlerMap = getJdbcHandlerMapForEnumInterfaces(clazz, clazz);
if (jdbcHandlerMap == null) {
register(clazz, getInstance(clazz, defaultEnumTypeHandler));
return TYPE_HANDLER_MAP.get(clazz);
}
} else {
//查詢父類對應的TypeHandler集合,作為初始化集合
jdbcHandlerMap = getJdbcHandlerMapForSuperclass(clazz);
}
}
TYPE_HANDLER_MAP.put(type, jdbcHandlerMap == null ? NULL_TYPE_HANDLER_MAP : jdbcHandlerMap);
return jdbcHandlerMap;
}
private Map<JdbcType, TypeHandler<?>> getJdbcHandlerMapForEnumInterfaces(Class<?> clazz, Class<?> enumClazz) {
for (Class<?> iface : clazz.getInterfaces()) {
Map<JdbcType, TypeHandler<?>> jdbcHandlerMap = TYPE_HANDLER_MAP.get(iface);
if (jdbcHandlerMap == null) {
//遞迴呼叫
jdbcHandlerMap = getJdbcHandlerMapForEnumInterfaces(iface, enumClazz);
}
if (jdbcHandlerMap != null) {
// Found a type handler regsiterd to a super interface
HashMap<JdbcType, TypeHandler<?>> newMap = new HashMap<JdbcType, TypeHandler<?>>();
for (Entry<JdbcType, TypeHandler<?>> entry : jdbcHandlerMap.entrySet()) {
// Create a type handler instance with enum type as a constructor arg
newMap.put(entry.getKey(), getInstance(enumClazz, entry.getValue().getClass()));
}
return newMap;
}
}
return null;
}
private Map<JdbcType, TypeHandler<?>> getJdbcHandlerMapForSuperclass(Class<?> clazz) {
Class<?> superclass = clazz.getSuperclass();
if (superclass == null || Object.class.equals(superclass)) {
return null;
}
Map<JdbcType, TypeHandler<?>> jdbcHandlerMap = TYPE_HANDLER_MAP.get(superclass);
if (jdbcHandlerMap != null) {
return jdbcHandlerMap;
} else {
//遞迴
return getJdbcHandlerMapForSuperclass(superclass);
}
}
2、如果只是註冊了一個Typehandler則使用此Typehandler物件
private TypeHandler<?> pickSoleHandler(Map<JdbcType, TypeHandler<?>> jdbcHandlerMap) {
TypeHandler<?> soleHandler = null;
for (TypeHandler<?> handler : jdbcHandlerMap.values()) {
if (soleHandler == null) {
soleHandler = handler;
} else if (!handler.getClass().equals(soleHandler.getClass())) {
// More than one type handlers registered.
return null;
}
}
return soleHandler;
}
- 同樣還可以在config配置檔案中自定義TypeHandler介面實現類,mybatis
初始化時候會自動註冊到TypeHandlerRegistry。
三、TypeAliaRegistry
- 別名註冊型別,通過該類完成別名註冊和管理功能。主要通過TYPE_ALIASES欄位管理別名和Java型別直接的對對應關係
- 構造器中預設註冊了很多的別名
通過registerAliases完成別名註冊
//掃面指定包下面的所有類,併為執行類新增別名
public void registerAliases(String packageName, Class<?> superType){
ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<Class<?>>();
//查詢指定包下的superType型別物件
resolverUtil.find(new ResolverUtil.IsA(superType), packageName);
Set<Class<? extends Class<?>>> typeSet = resolverUtil.getClasses();
for(Class<?> type : typeSet){
// 過濾掉內部類及介面歐和抽象類
if (!type.isAnonymousClass() && !type.isInterface() && !type.isMemberClass()) {
registerAlias(type);
}
}
}
public void registerAlias(Class<?> type) {
String alias = type.getSimpleName();
//讀取@Alias的註解
Alias aliasAnnotation = type.getAnnotation(Alias.class);
if (aliasAnnotation != null) {
alias = aliasAnnotation.value();
}
registerAlias(alias, type);
}
public void registerAlias(String alias, Class<?> value) {
if (alias == null) {
throw new TypeException("The parameter alias cannot be null");
}
// 忽略大小寫
String key = alias.toLowerCase(Locale.ENGLISH);
if (TYPE_ALIASES.containsKey(key) && TYPE_ALIASES.get(key) != null && !TYPE_ALIASES.get(key).equals(value)) {
throw new TypeException("The alias '" + alias + "' is already mapped to the value '" + TYPE_ALIASES.get(key).getName() + "'.");
}
TYPE_ALIASES.put(key, value);
}
四、總結
在mybatis中定義了一套機制來出來相關的型別轉換
1、使用TypeHandler介面定義了JDBCType和JavaType之間的轉換關係
2、使用TypeHandlerRegistry來對TypeHandler進行註冊和管理
3、使用TypeAliaRegistry來進行mybatis中類別名的註冊和管理
相關文章
- mybaits原始碼分析--型別轉換模組(三)AI原始碼型別
- myBatis原始碼解析-型別轉換篇(5)MyBatis原始碼型別
- Swoole 原始碼分析——基礎模組之 Pipe 管道原始碼
- [Java基礎]之 資料型別轉換Java資料型別
- java基礎型別原始碼解析之HashMapJava型別原始碼HashMap
- Javascript基礎之-強制型別轉換(三)JavaScript型別
- Javascript基礎之-強制型別轉換(一)JavaScript型別
- java基礎:型別轉換castJava型別AST
- 精盡 MyBatis 原始碼分析 - 基礎支援層MyBatis原始碼
- Javascript基礎:變數型別轉換JavaScript變數型別
- Python基礎之集合和資料型別轉換Python資料型別
- [基礎] JavaScript 型別轉換及面試題JavaScript型別面試題
- Presto原始碼分析之資料型別REST原始碼資料型別
- (一) Mybatis原始碼分析-解析器模組MyBatis原始碼
- java 基礎型別與byte[]的轉換Java型別
- javascript基礎(基本資料型別轉換)(六)JavaScript資料型別
- Mybatis實踐(一)型別轉換器MyBatis型別
- MyBatis使用自定義TypeHandler轉換型別MyBatis型別
- JAVA基礎:Java變數型別間的相互轉換(轉)Java變數型別
- Zepto原始碼分析之form模組原始碼ORM
- lodash原始碼分析之獲取資料型別原始碼資料型別
- 強制型別轉換之(==)型別
- Mybatis使用小技巧-自定義型別轉換器MyBatis型別
- Mybatis原始碼分析MyBatis原始碼
- Python3 基礎學習之基本數值賦值、型別轉換Python賦值型別
- 精盡MyBatis原始碼分析 - MyBatis-Spring 原始碼分析MyBatis原始碼Spring
- MyBatis原始碼分析之核心處理層MyBatis原始碼
- Swoole 原始碼分析——Client模組之Recv原始碼client
- Swoole 原始碼分析——Client模組之Send原始碼client
- [.net 物件導向程式設計基礎] (4) 基礎中的基礎——資料型別轉換物件程式設計資料型別
- PHP 型別轉換&&型別強制轉換PHP型別
- mybatis原始碼解析(五) --- typehandler註冊和處理的查詢結果物件的型別轉換MyBatis原始碼物件型別
- java基礎:ArrayList — 原始碼分析Java原始碼
- java基礎:HashMap — 原始碼分析JavaHashMap原始碼
- java基礎:Enum — 原始碼分析Java原始碼
- java基礎:Integer — 原始碼分析Java原始碼
- java基礎:TreeMap — 原始碼分析Java原始碼
- Java基礎——HashMap原始碼分析JavaHashMap原始碼