最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Mybatis?TypeHandler接口及繼承關(guān)系示例解析

 更新時(shí)間:2023年02月08日 10:53:33   作者:念念清晰  
這篇文章主要為大家介紹了Mybatis?TypeHandler接口及繼承關(guān)系示例解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

開(kāi)篇

JDBC類(lèi)型與Java類(lèi)型并不是完全一一對(duì)應(yīng)的。所以在PreparedStatement綁定參數(shù)的時(shí)候需要把Java類(lèi)型轉(zhuǎn)為JDBC類(lèi)型。JDBC類(lèi)型的枚舉值在JdbcType枚舉值中存儲(chǔ)。

MyBatis中提供了一個(gè)接口專用于JDBC類(lèi)型與Java類(lèi)型的轉(zhuǎn)換。它就是我們今天的主題:TypeHandler(類(lèi)型轉(zhuǎn)換器)

TypeHandler接口

TypeHandler是用于JDBC類(lèi)型與Java類(lèi)型的轉(zhuǎn)換

我們先來(lái)看一下這個(gè)接口的定義,它都規(guī)范了哪些行為。再來(lái)說(shuō)這些方法的實(shí)現(xiàn)類(lèi)和具體作用。

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;
}

該接口中共有4個(gè)方法。這四個(gè)方法只定義了兩種行為,一是setParameter(設(shè)置參數(shù))二是操作結(jié)果集獲取指定對(duì)象

方法名描述
setParameter使用PreparedStatement執(zhí)行SQL是,設(shè)置帶?的參數(shù)。
getResult獲取結(jié)果集中指定列名對(duì)應(yīng)的值,或者獲取結(jié)果集中指定索引對(duì)應(yīng)的值

實(shí)際setParameter方法的底層實(shí)現(xiàn)就是JDBC操作

PreparedStatement ps = connection.prepareStatement();
ps.setString(1,"value");

getResult方法的底層同樣也是JDBC操作

// rs是結(jié)果集對(duì)象ResultSet
rs.getInt(colName);
rs.getInt(i)

TypeHandler繼承體系

在MyBatis中JDBC類(lèi)型被定義在枚舉類(lèi)JdbcType中。共有41中JDBC類(lèi)型。每個(gè)JDBC類(lèi)型都對(duì)應(yīng)一個(gè)XxxTypeHandler類(lèi)。每個(gè)類(lèi)都是解決特定的JDBC類(lèi)型轉(zhuǎn)換

TypeHandler接口值規(guī)定了行為,每種JDBC類(lèi)型,都提供了對(duì)應(yīng)的實(shí)現(xiàn)類(lèi)來(lái)完場(chǎng)JDBC到Java類(lèi)型的轉(zhuǎn)換。

每個(gè)TypeHandler的實(shí)現(xiàn)都大同小異,而為了避免空指針的問(wèn)題,TypeHandler還有一個(gè)抽象子類(lèi),對(duì)這4個(gè)方法做了模板處理。

  • 設(shè)置參數(shù):如果值為null則直接跳過(guò),否則調(diào)用具體實(shí)現(xiàn)類(lèi)設(shè)置參數(shù)
  • 從結(jié)果集中獲取數(shù)據(jù):調(diào)用具體實(shí)現(xiàn)類(lèi)的getNullableResult方法

BaseTypeHandler的代碼比較簡(jiǎn)單易懂,這列舉重要實(shí)現(xiàn):

public void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException {
  if (parameter == null) {
    if (jdbcType == null) {拋異常}
    try {
      ps.setNull(i, jdbcType.TYPE_CODE);
    } catch (SQLException e) { 拋異常 }
  } else {
    try {
      // 如果參數(shù)不為Null才調(diào)用子類(lèi)方法
      setNonNullParameter(ps, i, parameter, jdbcType);
    } catch (Exception e) { 拋異常 }
  }
}

我們先來(lái)剖析下常用的類(lèi)型。IntegerTypeHandler

IntegerTypeHandler

public class IntegerTypeHandler extends BaseTypeHandler<Integer> {
  @Override
  public void setNonNullParameter(PreparedStatement ps, int i, Integer parameter, JdbcType jdbcType) throws SQLException {
    ps.setInt(i, parameter);
  }
  public Integer getNullableResult(ResultSet rs, String columnName) throws SQLException {
    int result = rs.getInt(columnName);
    return result == 0 && rs.wasNull() ? null : result;
  }
  // 省略另外兩個(gè)方法
}

可以看到IntegerTypeHandler的底層就是調(diào)用了JDBC對(duì)象PreparedStatement的方法來(lái)設(shè)置參數(shù)與獲取結(jié)果集中的記錄。這個(gè)實(shí)現(xiàn)是最簡(jiǎn)單的一個(gè),就不展開(kāi)介紹了。

下面我們?cè)賮?lái)看一下日期類(lèi)型的映射

DateTypeHandler

public class DateTypeHandler extends BaseTypeHandler<Date> {
  @Override
  public void setNonNullParameter(PreparedStatement ps, int i, Date parameter, JdbcType jdbcType) throws SQLException {
    ps.setTimestamp(i, new Timestamp(parameter.getTime()));
  }
  @Override
  public Date getNullableResult(ResultSet rs, String columnName) throws SQLException {
    Timestamp sqlTimestamp = rs.getTimestamp(columnName);
    if (sqlTimestamp != null) {
      return new Date(sqlTimestamp.getTime());
    }
    return null;
  }
}
  • 設(shè)置參數(shù):我們使用Java編程,使用的日期常用的類(lèi)是java.util.Date,但是在JDBC操作數(shù)據(jù)庫(kù)的時(shí)候,往往需要的不是Date類(lèi)型,而是SQL標(biāo)準(zhǔn)定義的Timestamp類(lèi)型,DateTypeHandler會(huì)幫助我們把Date轉(zhuǎn)化為T(mén)imestamp
  • 獲取結(jié)果,從JDBC中獲取的記錄如果是Timestamp類(lèi)型,則DateTypeHandler幫我們轉(zhuǎn)為java.util.Date對(duì)象

注:可能大家在編程JDBC的時(shí)候,執(zhí)行如下語(yǔ)句select * from user where birthday > ?,無(wú)論數(shù)據(jù)庫(kù)中birthday字段是Date/DateTime/TimeStamp類(lèi)型,我們都會(huì)把會(huì)把?的值設(shè)置為Date類(lèi)型,實(shí)際上這是不符合JDBC標(biāo)準(zhǔn)的。

其他TypeHandler的實(shí)現(xiàn),就不再看了。邏輯都是一樣的。

TypeHandlerRegistry

通過(guò)上一小節(jié)了解到了每一種JDBC類(lèi)型,都提供了TypeHandler的實(shí)現(xiàn)。那么mybatis什么時(shí)候才會(huì)用到這些實(shí)現(xiàn)類(lèi)呢?或者說(shuō)當(dāng)需要進(jìn)行類(lèi)型轉(zhuǎn)換的時(shí)候,MyBatis是如何使用這些實(shí)現(xiàn)類(lèi)的。

在MyBatis初始化的時(shí)候,會(huì)加載TypeHandlerRegistry這個(gè)類(lèi),這個(gè)類(lèi)有在構(gòu)造方法里會(huì)調(diào)用一系列的register方法把TypeHandler所有實(shí)現(xiàn)類(lèi)都注冊(cè)到TypeHandlerRegistry中。下面我們先來(lái)看一下TypeHandlerRegistry中的重要屬性

public final class TypeHandlerRegistry {
  private final Map<JdbcType, TypeHandler<?>> jdbcTypeHandlerMap = new EnumMap<>(JdbcType.class);
  private final Map<Type, Map<JdbcType, TypeHandler<?>>> typeHandlerMap = new ConcurrentHashMap<>();
  private final TypeHandler<Object> unknownTypeHandler;
  private final Map<Class<?>, TypeHandler<?>> allTypeHandlersMap = new HashMap<>();
}
  • jdbcTypeHandlerMap:key是Jdbc類(lèi)型,value是Jdbc類(lèi)型對(duì)應(yīng)的處理器
  • typeHandlerMap:key是Java類(lèi)型,value是對(duì)應(yīng)的處理器集合(它也是要給Map)。因?yàn)橐粋€(gè)Java類(lèi)型可能被多個(gè)處理器解析。比如String類(lèi)型可能被解析為char varchar類(lèi)型等。比如Java的java.util.Date類(lèi)型可以被解析為Date Time TimeStamp類(lèi)型。存在一對(duì)多的關(guān)系
  • unknownTypeHandler:位置的TypeHandler,一般為空
  • allTypeHandlersMap:所有類(lèi)型處理器,key是TypeHandler實(shí)現(xiàn)類(lèi)的Class對(duì)象,value是具體的TypeHandler

TypeHandlerRegistry#register方法

上面介紹了TypeHandlerRegistry中幾個(gè)重要的屬性。在mybatis啟動(dòng)時(shí),就會(huì)把所有的TypeHandler都注冊(cè)到如上的4個(gè)數(shù)據(jù)結(jié)構(gòu)中。具體的實(shí)現(xiàn)在TypeHandlerRegistry的構(gòu)造方法中,調(diào)用register方法進(jìn)行注冊(cè)TypeHandler

public TypeHandlerRegistry(Configuration configuration) {
  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());
  register(Short.class, new ShortTypeHandler());
  register(short.class, new ShortTypeHandler());
  register(JdbcType.SMALLINT, new ShortTypeHandler());
  // ...省略其他register
}

這些register最終將如上的3個(gè)Map數(shù)據(jù)結(jié)構(gòu)填充。通過(guò)名字也能看出來(lái),注冊(cè)Class/JdbcType與TypeHandler的映射關(guān)系。而這些register分為兩大類(lèi),一類(lèi)是注冊(cè)JDBC-TypeHandler映射關(guān)系,一類(lèi)是注冊(cè)Java-TypeHandler的映射關(guān)系。我們先來(lái)看第一種

public void register(JdbcType jdbcType, TypeHandler<?> handler) {
  jdbcTypeHandlerMap.put(jdbcType, handler);
}

它的實(shí)現(xiàn)很簡(jiǎn)單,就是直接把jdbcType作為key,TypeHandler作為value,存入jdbcTypeHandlerMap即可。接下來(lái)我們?cè)賮?lái)看第二種:注冊(cè)Java-TypeHandler的映射關(guān)系

private <T> void register(Type javaType, TypeHandler<? extends T> typeHandler) {
  MappedJdbcTypes mappedJdbcTypes = typeHandler.getClass().getAnnotation(MappedJdbcTypes.class);
  if (mappedJdbcTypes != null) {
    for (JdbcType handledJdbcType : mappedJdbcTypes.value()) {
      register(javaType, handledJdbcType, typeHandler);
    }
    if (mappedJdbcTypes.includeNullJdbcType()) {
      register(javaType, null, typeHandler);
    }
  } else {
    register(javaType, null, typeHandler);
  }
}

我們來(lái)分析下 注冊(cè)步驟:

  • 獲取MappedJdbcTypes注解,對(duì)自定義的TypeHandler進(jìn)行注冊(cè)。(自定義TypeHandler時(shí)需要該注解指定自定義的TypeHandler都解析哪些JDBC類(lèi)型)
  • 如果TypeHandler沒(méi)有注解信息(也就是沒(méi)有自定義TypeHandler)則直接調(diào)用重載register方法注冊(cè)Java-TypeHandler的映射關(guān)系。

下面就來(lái)看一下這個(gè)重載register方法

private void register(Type javaType, JdbcType jdbcType, TypeHandler<?> handler) {
  if (javaType != null) {
    Map<JdbcType, TypeHandler<?>> map = typeHandlerMap.get(javaType);
    if (map == null || map == NULL_TYPE_HANDLER_MAP) {
      map = new HashMap<>();
    }
    map.put(jdbcType, handler);
    typeHandlerMap.put(javaType, map);
  }
  allTypeHandlersMap.put(handler.getClass(), handler);
}

代碼中它主要做了兩件事:

  • 注冊(cè)Java-TypeHandler的映射關(guān)系
  • 把TypeHandler的Class和類(lèi)的映射關(guān)系存入allTypeHandlersMap

至此TypeHandlerRegistry中就有了最重要的兩種映射關(guān)系

  • JDBC類(lèi)型 與 TypeHandler 的映射關(guān)系(通過(guò)Jdbc類(lèi)型尋找合適的TypeHandler類(lèi)型,通常用于結(jié)果集的解析過(guò)程)
  • Java類(lèi)型 與 TypeHandler 的映射關(guān)系(通過(guò)Java類(lèi)型尋找合適的TypeHandler類(lèi)型,通常用于為PreparedStatement對(duì)象設(shè)置參數(shù))

TypeHandlerRegistry也提供了一系列的get*方法,可以根據(jù)指定的信息返回需要的TypeHandler類(lèi)

  • getMappingTypeHandler(Class>):根據(jù)TypeHandler類(lèi)型獲取對(duì)應(yīng)的TypeHandler對(duì)象
  • getTypeHandler(JdbcType):根據(jù)JdbcType類(lèi)型獲取TypeHandler對(duì)象
  • getTypeHandler(Class):根據(jù)Class類(lèi)型獲取TypeHandler對(duì)象
  • getJdbcHandlerMap(Type):根據(jù)Java類(lèi)型獲取TypeHandler對(duì)象

總結(jié)

TypeHandler是類(lèi)型處理器,它用來(lái)解析Java類(lèi)型與Jdbc類(lèi)型之間的相互轉(zhuǎn)換。

TypeHandlerRegistry是一個(gè)注冊(cè)器,其中注冊(cè)了JDBC與TypeHandler的映射關(guān)系、Java類(lèi)型與TypeHandler的映射關(guān)系。

那么由此我們可以想象到,在mybatis執(zhí)行SQL的過(guò)程中,一定會(huì)在某處調(diào)用TypeHandlerRegistry并通過(guò)參數(shù)的Java類(lèi)型獲取對(duì)應(yīng)的TypeHandler對(duì)象為PreparedStatement設(shè)置參數(shù)。

也一定會(huì)在解析結(jié)果集的過(guò)程中,調(diào)用TypeHandlerRegistry并通過(guò)結(jié)果集數(shù)據(jù)的Jdbc類(lèi)型獲取對(duì)應(yīng)的TypeHandler對(duì)象為來(lái)解析結(jié)果集中的記錄

以上就是Mybatis TypeHandler接口及繼承關(guān)系示例解析的詳細(xì)內(nèi)容,更多關(guān)于Mybatis TypeHandler接口繼承的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Java Springboot 后端使用Mockito庫(kù)進(jìn)行單元測(cè)試流程分析

    Java Springboot 后端使用Mockito庫(kù)進(jìn)行單元測(cè)試流程分析

    使用Mock進(jìn)行單元測(cè)試可以避免啟動(dòng)整個(gè)Spring框架,節(jié)省時(shí)間并降低外部依賴影響,Mock允許模擬外部方法和類(lèi),專注于測(cè)試方法的功能邏輯,本文給大家介紹Java Springboot 后端使用Mockito庫(kù)進(jìn)行單元測(cè)試流程分析,感興趣的朋友跟隨小編一起看看吧
    2024-10-10
  • springboot中使用自定義兩級(jí)緩存的方法

    springboot中使用自定義兩級(jí)緩存的方法

    這篇文章主要介紹了springboot中使用自定義兩級(jí)緩存的方法,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2018-05-05
  • SpringBoot基于redis自定義注解實(shí)現(xiàn)后端接口防重復(fù)提交校驗(yàn)

    SpringBoot基于redis自定義注解實(shí)現(xiàn)后端接口防重復(fù)提交校驗(yàn)

    本文主要介紹了SpringBoot基于redis自定義注解實(shí)現(xiàn)后端接口防重復(fù)提交校驗(yàn),文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-01-01
  • 詳解JDBC使用

    詳解JDBC使用

    JDBC(Java Database Connectivity),即Java數(shù)據(jù)庫(kù)連接,是一種用于執(zhí)行SQL語(yǔ)句的Java API,可以為多種關(guān)系數(shù)據(jù)庫(kù)提供同一訪問(wèn),它由一組用Java語(yǔ)言編寫(xiě)的類(lèi)和接口組成。
    2017-05-05
  • Spring MVC下 bootStrap服務(wù)器分頁(yè)代碼

    Spring MVC下 bootStrap服務(wù)器分頁(yè)代碼

    因?yàn)镾pring 對(duì)于ajax直接返回對(duì)象,到了WEB頁(yè)面就轉(zhuǎn)換成json 所以不需要使用JSON轉(zhuǎn)換封裝可以直接使用。接下來(lái)通過(guò)本文給大家分享Spring MVC下 bootStrap服務(wù)器分頁(yè)代碼,需要的的朋友參考下
    2017-03-03
  • Java中printStackTrace()用法示例

    Java中printStackTrace()用法示例

    這篇文章主要給大家介紹了關(guān)于Java中printStackTrace()用法的相關(guān)資料,printStackTrace()方法一般與拋出異常搭配使用,效果是打印出異常位置,需要的朋友可以參考下
    2024-05-05
  • 詳解shiro緩存機(jī)制

    詳解shiro緩存機(jī)制

    Shiro提供了類(lèi)似于Spring的Cache抽象,即Shiro本身不實(shí)現(xiàn)Cache,但是對(duì)Cache進(jìn)行了又抽象,方便更換不同的底層Cache實(shí)現(xiàn)。下面通過(guò)實(shí)例代碼給大家分享shiro緩存機(jī)制,感興趣的朋友一起看看吧
    2017-09-09
  • Spring Security OAuth過(guò)期的解決方法

    Spring Security OAuth過(guò)期的解決方法

    這篇文章主要介紹了Spring Security OAuth過(guò)期的解決方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-09-09
  • 基于@RequestBody注解只能注入對(duì)象和map的解決

    基于@RequestBody注解只能注入對(duì)象和map的解決

    這篇文章主要介紹了@RequestBody注解只能注入對(duì)象和map的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-10-10
  • 實(shí)例解析JAVA中代碼的加載順序

    實(shí)例解析JAVA中代碼的加載順序

    這篇文章主要介紹了舉例說(shuō)明Java中代碼塊的執(zhí)行順序,需要的朋友可以參考下
    2017-04-04

最新評(píng)論

莎车县| 太仓市| 金门县| 赤峰市| 高碑店市| 蛟河市| 鄂尔多斯市| 南江县| 疏勒县| 边坝县| 潜江市| 三明市| 阿坝县| 龙泉市| 布尔津县| 江口县| 西藏| 兴业县| 尼勒克县| 吕梁市| 临江市| 神农架林区| 濮阳市| 边坝县| 象州县| 锦屏县| 郧西县| 瓮安县| 茶陵县| 湖南省| 磴口县| 林西县| 太和县| 龙口市| 余姚市| 剑阁县| 安国市| 高雄县| 乌恰县| 榆树市| 平舆县|