Mybatis 文檔篇 2.4:Configuration 之 TypeHandlers

1 Configuration Structure

2 TypeHandlers

2.1 默認(rèn)的 TypeHandler

Whenever MyBatis sets a parameter on a PreparedStatement or retrieves a value from a ResultSet, a TypeHandler is used to retrieve the value in a means appropriate to the Java type.
作用:無論是 Mybatis 在 PreparedStatement 設(shè)置參數(shù)還是從 ResultSet 獲取值,TypeHandler 都會將獲取到的值以一個合適的方式轉(zhuǎn)換成 Java 類型。

默認(rèn)的 TypeHandler 對應(yīng)表

2.2 自定義 TypeHandler

You can override the type handlers or create your own to deal with unsupported or non-standard types.
你可以重寫類型處理器或者創(chuàng)建你自己的類型處理器來處理不支持的或者非標(biāo)準(zhǔn)的類型。

To do so, implement the interface org.apache.ibatis.type.TypeHandler or extend the convenience class org.apache.ibatis.type.BaseTypeHandler and optionally map it to a JDBC type.
具體方法是:實現(xiàn)接口 org.apache.ibatis.type.TypeHandler 或者繼承類 org.apache.ibatis.type.BaseTypeHandler ,選擇性地將它映射到 JDBC 類型。

// ExampleTypeHandler.java
@MappedJdbcTypes(JdbcType.VARCHAR)
public class ExampleTypeHandler extends BaseTypeHandler<String> {

  @Override
  public void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType) throws SQLException {
    ps.setString(i, parameter);
  }

  @Override
  public String getNullableResult(ResultSet rs, String columnName) throws SQLException {
    return rs.getString(columnName);
  }

  @Override
  public String getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
    return rs.getString(columnIndex);
  }

  @Override
  public String getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
    return cs.getString(columnIndex);
  }

}
<!-- mybatis-config.xml -->
<typeHandlers>
  <typeHandler handler="org.mybatis.example.ExampleTypeHandler"/>
</typeHandlers>

Using such a TypeHandler would override the existing type handler for Java String properties and VARCHAR parameters and results.
使用這樣一個 TypeHandler 將覆蓋已經(jīng)存在的處理 Java String 類型屬性和 VARCHAR 參數(shù)和結(jié)果集的類型處理器。

2.3 MyBatis 如何查找 TypeHandler

Note that MyBatis does not introspect upon the database metadata to determine the type, so you must specify that it’s a VARCHAR field in the parameter and result mappings to hook in the correct type handler.
注意,MyBatis 不會窺探數(shù)據(jù)庫元信息來決定使用哪種類型,所以你必須在參數(shù)和結(jié)果集中指定這是個 VARCHAR 類型的字段,從而可以使用正確的類型處理器。

This is due to the fact that MyBatis is unaware of the data type until the statement is executed.
這是因為 MyBatis 直到語句執(zhí)行才知道數(shù)據(jù)類型。

2.3.1 如何知道 Java Type

MyBatis will know the Java type that you want to handle with this TypeHandler by introspecting its generic type, but you can override this behavior by two means:
MyBatis 會通過查看泛型來知道你想要通過該 TypeHandler 處理的 Java 類型,你也可以通過以下兩種方式覆蓋這個行為:

  • Adding a javaType attribute to the typeHandler element (for example: javaType="String")
    在 mybatis-config.xml 中的 typeHandler 元素中添加 javaType 屬性。(例如:javaType="String")

  • Adding a @MappedTypes annotation to your TypeHandler class specifying the list of java types to associate it with. This annotation will be ignored if the javaType attribute as also been specified.
    在 TypeHandler 類中通過加入 @MappedTypes 注解的方式指定關(guān)聯(lián)的 Java 類型列表。如果已經(jīng)設(shè)置了 javaType 屬性,這個注解將被忽略。
    如:@MappedTypes({Integer.class, Double.class})

2.3.2 如何知道 JDBC Type

Associated JDBC type can be specified by two means:
有以下兩種方式可指定 JDBC 類型:

  • Adding a jdbcType attribute to the typeHandler element (for example: jdbcType="VARCHAR").
    在 typeHandler 元素中添加 jdbcType 屬性。(例如:jdbcType="VARCHAR")

  • Adding a @MappedJdbcTypes annotation to your TypeHandler class specifying the list of JDBC types to associate it with. This annotation will be ignored if the jdbcType attribute as also been specified.
    在你的 TypeHandler 類中添加 @MappedJdbcTypes 注解指定要關(guān)聯(lián)的 JDBC 類型列表。如果已經(jīng)設(shè)置了 jdbcType 屬性,這個注解將被忽略。
    如:@MappedJdbcTypes({JdbcType.INTEGER, JdbcType.DOUBLE})

2.3.3 如何選擇 TypeHandler

When deciding which TypeHandler to use in a ResultMap, the Java type is known (from the result type), but the JDBC type is unknown.
當(dāng)決定在 ResultMap 中使用哪個 TypeHandler 時,Java 類型是已知的(從結(jié)果類型中知道),但 JDBC 類型是未知的。

MyBatis therefore uses the combination javaType=[TheJavaType], jdbcType=null to choose a TypeHandler.
因此,MyBatis 使用 javaType=[TheJavaType], jdbcType=null 的組合去選擇一個 TypeHandler。

This means that using a @MappedJdbcTypes annotation restricts the scope of a TypeHandler and makes it unavailable for use in ResultMaps unless explicity set.
這就意味著使用 @MappedJdbcTypes 注解可以限制 TypeHandler 的范圍,除非顯式設(shè)置,否則 TypeHandler 在 ResultMap 中是無效的。

To make a TypeHandler available for use in a ResultMap, set includeNullJdbcType=true on the @MappedJdbcTypes annotation.
為了使 TypeHandler 在 ResultMap 中有效,在 @MappedJdbcTypes 注解上設(shè)置 includeNullJdbcType=true 即可。

Since Mybatis 3.4.0 however, if a single TypeHandler is registered to handle a Java type, it will be used by default in ResultMaps using this Java type (i.e. even without includeNullJdbcType=true).
但是從 Mybatis 3.4.0 開始,如果單個 TypeHandler 只被注冊綁定給一個 Java 類型,它將在使用該 Java 類型的 ResultMap 中被默認(rèn)使用。不用設(shè)置 includeNullJdbcType=true)

And finally you can let MyBatis search for your TypeHandlers:
最后你可以讓 MyBatis 來查找你的 TypeHandler:

<!-- mybatis-config.xml -->
<typeHandlers>
  <package name="org.mybatis.example"/>
</typeHandlers>

Note that when using the autodiscovery feature JDBC types can only be specified with annotations.
注意,當(dāng)使用自動檢索功能時,JDBC 類型僅能通過注解方式被指定。

2.4 創(chuàng)建可以處理多個類的 TypeHandler

You can create a generic TypeHandler that is able to handle more than one class.
作用:你可以創(chuàng)建一個可以處理多個類的泛型類型處理器。

For that purpose add a constructor that receives the class as a parameter and MyBatis will pass the actual class when constructing the TypeHandler.
怎么做:為了使用泛型類型處理器,需要增加一個接收該類的 class 作為參數(shù)的構(gòu)造器,Mybatis 會在構(gòu)造該 TypeHandler 時傳遞一個具體的類。

//GenericTypeHandler.java
public class GenericTypeHandler<E extends MyObject> extends BaseTypeHandler<E> {
  private Class<E> type;
  public GenericTypeHandler(Class<E> type) {
    if (type == null) throw new IllegalArgumentException("Type argument cannot be null");
    this.type = type;
  }
  ...
}

EnumTypeHandler and EnumOrdinalTypeHandler are generic TypeHandlers.
EnumTypeHandler 和 EnumOrdinalTypeHandler 就是泛型類型構(gòu)造器。

2.5 Handling Enums

If you want to map an Enum, you'll need to use either EnumTypeHandler or EnumOrdinalTypeHandler.For example, let's say that we need to store the rounding mode that should be used with some number if it needs to be rounded.
使用場景:如果你想要映射一個枚舉類型,你將會用到 EnumTypeHandler 或 EnumOrdinalTypeHandler。比如,我們想使用 RoundingMode 來存取數(shù)字的近似值(java.math.RoundingMode 是一個 Enum)。

By default, MyBatis uses EnumTypeHandler to convert the Enum values to their names.
默認(rèn)情況下,MyBatis 使用 EnumTypeHandler 轉(zhuǎn)換枚舉值到對應(yīng)的名字。

Note EnumTypeHandler is special in the sense that unlike other handlers, it does not handle just one specific class, but any class that extends Enum.
注意,在某種意義上,相比于其他的處理器,EnumTypeHandler 是特殊的,它不止能處理某個特定的類,而是能處理所有繼承自 Enum 的類。

However, we may not want to store names. Our DBA may insist on an integer code instead. That's just as easy: add EnumOrdinalTypeHandler to the typeHandlers in your config file, and now each RoundingMode will be mapped to an integer using its ordinal value.
但是,我們可能不想要存儲名字。我們的 DBA 可能堅持要使用整型值來替代它。那也一樣簡單:給配置文件中的 typeHandlers 添加 EnumOrdinalTypeHandler 即可,這樣每個 RoundingMode 將通過它們的序數(shù)值映射成對應(yīng)的數(shù)值。

<!-- mybatis-config.xml -->
<typeHandlers>
  <typeHandler handler="org.apache.ibatis.type.EnumOrdinalTypeHandler" javaType="java.math.RoundingMode"/>
</typeHandlers>

But what if you want to map the same Enum to a string in one place and to integer in another?
但是萬一你想要將同樣的 Enum 同時映射成字符串類型和整型呢?

The auto-mapper will automatically use EnumOrdinalTypeHandler, so if we want to go back to using plain old ordinary EnumTypeHandler, we have to tell it, by explicitly setting the type handler to use for those SQL statements.
自動映射器會自動使用 EnumOrdinalTypeHandler,所以如果我們想使用普通的 EnumTypeHandler,我們就需要在語句中顯式地定義該類型處理器。

<!DOCTYPE mapper
    PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
    "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="org.apache.ibatis.submitted.rounding.Mapper">
        <resultMap type="org.apache.ibatis.submitted.rounding.User" id="usermap">
                <id column="id" property="id"/>
                <result column="name" property="name"/>
                <result column="funkyNumber" property="funkyNumber"/>
                <result column="roundingMode" property="roundingMode"/>
        </resultMap>

        <select id="getUser" resultMap="usermap">
                select * from users
        </select>

        <insert id="insert">
            insert into users (id, name, funkyNumber, roundingMode) values (
                #{id}, #{name}, #{funkyNumber}, #{roundingMode}
            )
        </insert>
        
        <resultMap type="org.apache.ibatis.submitted.rounding.User" id="usermap2">
                <id column="id" property="id"/>
                <result column="name" property="name"/>
                <result column="funkyNumber" property="funkyNumber"/>
                <result column="roundingMode" property="roundingMode" typeHandler="org.apache.ibatis.type.EnumTypeHandler"/>
        </resultMap>

        <select id="getUser2" resultMap="usermap2">
                select * from users2
        </select>

        <insert id="insert2">
            insert into users2 (id, name, funkyNumber, roundingMode) values (
                #{id}, #{name}, #{funkyNumber}, #{roundingMode, typeHandler=org.apache.ibatis.type.EnumTypeHandler}
            )
        </insert>
</mapper>

最后

說明:MyBatis 官網(wǎng)提供了簡體中文的翻譯,但個人覺得較為生硬,甚至有些地方邏輯不通,于是自己一個個重新敲著翻譯的(都不知道哪里來的自信...),有些地方同官網(wǎng)翻譯有出入,有些倔強地保留了自己的,有的實在別扭則保留了官網(wǎng)的,這些都會在實踐中一一更正。鑒于個人英文能力有限,文章中保留了官方文檔原英文介紹(個別地方加以調(diào)整修剪),希望有緣看到這里的朋友們能夠有自己的理解,不會被我可能錯誤或不合理的翻譯帶跑偏(〃'▽'〃),歡迎指正!

當(dāng)前版本:mybatis-3.5.0
官網(wǎng)文檔:MyBatis
官網(wǎng)翻譯:MyBatis 簡體中文
項目實踐:MyBatis Learn

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容