欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Mybatis?TypeHandler接口及繼承關系示例解析

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

開篇

JDBC類型與Java類型并不是完全一一對應的。所以在PreparedStatement綁定參數(shù)的時候需要把Java類型轉為JDBC類型。JDBC類型的枚舉值在JdbcType枚舉值中存儲。

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

TypeHandler接口

TypeHandler是用于JDBC類型與Java類型的轉換

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

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個方法。這四個方法只定義了兩種行為,一是setParameter(設置參數(shù))二是操作結果集獲取指定對象

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

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

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

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

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

TypeHandler繼承體系

在MyBatis中JDBC類型被定義在枚舉類JdbcType中。共有41中JDBC類型。每個JDBC類型都對應一個XxxTypeHandler類。每個類都是解決特定的JDBC類型轉換

TypeHandler接口值規(guī)定了行為,每種JDBC類型,都提供了對應的實現(xiàn)類來完場JDBC到Java類型的轉換。

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

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

BaseTypeHandler的代碼比較簡單易懂,這列舉重要實現(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才調用子類方法
      setNonNullParameter(ps, i, parameter, jdbcType);
    } catch (Exception e) { 拋異常 }
  }
}

我們先來剖析下常用的類型。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;
  }
  // 省略另外兩個方法
}

可以看到IntegerTypeHandler的底層就是調用了JDBC對象PreparedStatement的方法來設置參數(shù)與獲取結果集中的記錄。這個實現(xiàn)是最簡單的一個,就不展開介紹了。

下面我們再來看一下日期類型的映射

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ù):我們使用Java編程,使用的日期常用的類是java.util.Date,但是在JDBC操作數(shù)據(jù)庫的時候,往往需要的不是Date類型,而是SQL標準定義的Timestamp類型,DateTypeHandler會幫助我們把Date轉化為Timestamp
  • 獲取結果,從JDBC中獲取的記錄如果是Timestamp類型,則DateTypeHandler幫我們轉為java.util.Date對象

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

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

TypeHandlerRegistry

通過上一小節(jié)了解到了每一種JDBC類型,都提供了TypeHandler的實現(xiàn)。那么mybatis什么時候才會用到這些實現(xiàn)類呢?或者說當需要進行類型轉換的時候,MyBatis是如何使用這些實現(xiàn)類的。

在MyBatis初始化的時候,會加載TypeHandlerRegistry這個類,這個類有在構造方法里會調用一系列的register方法把TypeHandler所有實現(xiàn)類都注冊到TypeHandlerRegistry中。下面我們先來看一下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類型,value是Jdbc類型對應的處理器
  • typeHandlerMap:key是Java類型,value是對應的處理器集合(它也是要給Map)。因為一個Java類型可能被多個處理器解析。比如String類型可能被解析為char varchar類型等。比如Java的java.util.Date類型可以被解析為Date Time TimeStamp類型。存在一對多的關系
  • unknownTypeHandler:位置的TypeHandler,一般為空
  • allTypeHandlersMap:所有類型處理器,key是TypeHandler實現(xiàn)類的Class對象,value是具體的TypeHandler

TypeHandlerRegistry#register方法

上面介紹了TypeHandlerRegistry中幾個重要的屬性。在mybatis啟動時,就會把所有的TypeHandler都注冊到如上的4個數(shù)據(jù)結構中。具體的實現(xiàn)在TypeHandlerRegistry的構造方法中,調用register方法進行注冊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個Map數(shù)據(jù)結構填充。通過名字也能看出來,注冊Class/JdbcType與TypeHandler的映射關系。而這些register分為兩大類,一類是注冊JDBC-TypeHandler映射關系,一類是注冊Java-TypeHandler的映射關系。我們先來看第一種

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

它的實現(xiàn)很簡單,就是直接把jdbcType作為key,TypeHandler作為value,存入jdbcTypeHandlerMap即可。接下來我們再來看第二種:注冊Java-TypeHandler的映射關系

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

我們來分析下 注冊步驟:

  • 獲取MappedJdbcTypes注解,對自定義的TypeHandler進行注冊。(自定義TypeHandler時需要該注解指定自定義的TypeHandler都解析哪些JDBC類型)
  • 如果TypeHandler沒有注解信息(也就是沒有自定義TypeHandler)則直接調用重載register方法注冊Java-TypeHandler的映射關系。

下面就來看一下這個重載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);
}

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

  • 注冊Java-TypeHandler的映射關系
  • 把TypeHandler的Class和類的映射關系存入allTypeHandlersMap

至此TypeHandlerRegistry中就有了最重要的兩種映射關系

  • JDBC類型 與 TypeHandler 的映射關系(通過Jdbc類型尋找合適的TypeHandler類型,通常用于結果集的解析過程)
  • Java類型 與 TypeHandler 的映射關系(通過Java類型尋找合適的TypeHandler類型,通常用于為PreparedStatement對象設置參數(shù))

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

  • getMappingTypeHandler(Class>):根據(jù)TypeHandler類型獲取對應的TypeHandler對象
  • getTypeHandler(JdbcType):根據(jù)JdbcType類型獲取TypeHandler對象
  • getTypeHandler(Class):根據(jù)Class類型獲取TypeHandler對象
  • getJdbcHandlerMap(Type):根據(jù)Java類型獲取TypeHandler對象

總結

TypeHandler是類型處理器,它用來解析Java類型與Jdbc類型之間的相互轉換。

TypeHandlerRegistry是一個注冊器,其中注冊了JDBC與TypeHandler的映射關系、Java類型與TypeHandler的映射關系。

那么由此我們可以想象到,在mybatis執(zhí)行SQL的過程中,一定會在某處調用TypeHandlerRegistry并通過參數(shù)的Java類型獲取對應的TypeHandler對象為PreparedStatement設置參數(shù)。

也一定會在解析結果集的過程中,調用TypeHandlerRegistry并通過結果集數(shù)據(jù)的Jdbc類型獲取對應的TypeHandler對象為來解析結果集中的記錄

以上就是Mybatis TypeHandler接口及繼承關系示例解析的詳細內容,更多關于Mybatis TypeHandler接口繼承的資料請關注腳本之家其它相關文章!

相關文章

  • Java編程中二維數(shù)組的初始化和基本操作實例

    Java編程中二維數(shù)組的初始化和基本操作實例

    這篇文章主要介紹了Java編程中二維數(shù)組的初始化和基本操作實例,是Java入門學習中的基礎知識,需要的朋友可以參考下
    2015-10-10
  • java中使用DES加密解密實例

    java中使用DES加密解密實例

    這篇文章主要介紹了java中使用DES加密解密實例,需要的朋友可以參考一下
    2014-01-01
  • springboot集成nacos的配置方法

    springboot集成nacos的配置方法

    這篇文章主要介紹了springboot集成nacos的配置方法,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2020-03-03
  • 帶你用Java全面剖析類和對象

    帶你用Java全面剖析類和對象

    下面小編就為大家?guī)硪黄胬斫釰ava類和對象。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2021-09-09
  • InputStreamReader 和FileReader的區(qū)別及InputStream和Reader的區(qū)別

    InputStreamReader 和FileReader的區(qū)別及InputStream和Reader的區(qū)別

    這篇文章主要介紹了InputStreamReader 和FileReader的區(qū)別及InputStream和Reader的區(qū)別的相關資料,需要的朋友可以參考下
    2015-12-12
  • SpringBoot創(chuàng)建WebService方法詳解

    SpringBoot創(chuàng)建WebService方法詳解

    這篇文章主要介紹了SpringBoot如何創(chuàng)建WebService,文中有詳細的實現(xiàn)步驟以及示例代碼,對學習或工作有一定的幫助,需要的朋友跟著小編一起來學習吧
    2023-05-05
  • JAVA輸出流與輸入流代碼實例

    JAVA輸出流與輸入流代碼實例

    這篇文章主要介紹了JAVA輸出流與輸入流代碼實例,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2020-02-02
  • 解析spring事務管理@Transactional為什么要添加rollbackFor=Exception.class

    解析spring事務管理@Transactional為什么要添加rollbackFor=Exception.class

    這篇文章主要介紹了spring事務管理@Transactional為什么要添加rollbackFor=Exception.class,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2021-11-11
  • 如何將Java對象轉換為JSON實例詳解

    如何將Java對象轉換為JSON實例詳解

    有時候需要將對象轉換為JSON格式,所以這篇文章主要給大家介紹了關于如何將Java對象轉換為JSON的相關資料,文中通過實例代碼介紹的非常詳細,需要的朋友可以參考下
    2022-08-08
  • java讀取csv文件和寫csv示例分享

    java讀取csv文件和寫csv示例分享

    這篇文章主要介紹了JAVA對CSV格式文本數(shù)據(jù)處理后再保存成新CSV格式文本的模板,可以學習到java讀取csv文件和寫csv的方法,需要的朋友可以參考下
    2014-03-03

最新評論