MyBatis 3 XML配置-類型處理器

2022-04-07 15:04 更新

MyBatis 在設(shè)置預(yù)處理語句(?PreparedStatement?)中的參數(shù)或從結(jié)果集中取出一個值時, 都會用類型處理器將獲取到的值以合適的方式轉(zhuǎn)換成 Java 類型。下表描述了一些默認的類型處理器。

從 3.4.5 開始,MyBatis 默認支持 JSR-310(日期和時間 API) 。

類型處理器 Java 類型 JDBC 類型
BooleanTypeHandler java.lang.Booleanboolean 數(shù)據(jù)庫兼容的 BOOLEAN
ByteTypeHandler java.lang.Bytebyte 數(shù)據(jù)庫兼容的 NUMERIC 或 BYTE
ShortTypeHandler java.lang.Shortshort 數(shù)據(jù)庫兼容的 NUMERIC 或 SMALLINT
IntegerTypeHandler java.lang.Integerint 數(shù)據(jù)庫兼容的 NUMERIC 或 INTEGER
LongTypeHandler java.lang.Longlong 數(shù)據(jù)庫兼容的 NUMERIC 或 BIGINT
FloatTypeHandler java.lang.Floatfloat 數(shù)據(jù)庫兼容的 NUMERIC 或 FLOAT
DoubleTypeHandler java.lang.Doubledouble 數(shù)據(jù)庫兼容的 NUMERIC 或 DOUBLE
BigDecimalTypeHandler java.math.BigDecimal 數(shù)據(jù)庫兼容的 NUMERIC 或 DECIMAL
StringTypeHandler java.lang.String CHARVARCHAR
ClobReaderTypeHandler java.io.Reader -
ClobTypeHandler java.lang.String CLOBLONGVARCHAR
NStringTypeHandler java.lang.String NVARCHARNCHAR
NClobTypeHandler java.lang.String NCLOB
BlobInputStreamTypeHandler java.io.InputStream -
ByteArrayTypeHandler byte[] 數(shù)據(jù)庫兼容的字節(jié)流類型
BlobTypeHandler byte[] BLOBLONGVARBINARY
DateTypeHandler java.util.Date TIMESTAMP
DateOnlyTypeHandler java.util.Date DATE
TimeOnlyTypeHandler java.util.Date TIME
SqlTimestampTypeHandler java.sql.Timestamp TIMESTAMP
SqlDateTypeHandler java.sql.Date DATE
SqlTimeTypeHandler java.sql.Time TIME
ObjectTypeHandler Any OTHER 或未指定類型
EnumTypeHandler Enumeration Type VARCHAR 或任何兼容的字符串類型,用來存儲枚舉的名稱(而不是索引序數(shù)值)
EnumOrdinalTypeHandler Enumeration Type 任何兼容的 NUMERIC 或 DOUBLE 類型,用來存儲枚舉的序數(shù)值(而不是名稱)。
SqlxmlTypeHandler java.lang.String SQLXML
InstantTypeHandler java.time.Instant TIMESTAMP
LocalDateTimeTypeHandler java.time.LocalDateTime TIMESTAMP
LocalDateTypeHandler java.time.LocalDate DATE
LocalTimeTypeHandler java.time.LocalTime TIME
OffsetDateTimeTypeHandler java.time.OffsetDateTime TIMESTAMP
OffsetTimeTypeHandler java.time.OffsetTime TIME
ZonedDateTimeTypeHandler java.time.ZonedDateTime TIMESTAMP
YearTypeHandler java.time.Year INTEGER
MonthTypeHandler java.time.Month INTEGER
YearMonthTypeHandler java.time.YearMonth VARCHAR 或 LONGVARCHAR
JapaneseDateTypeHandler java.time.chrono.JapaneseDate DATE

你可以重寫已有的類型處理器或創(chuàng)建你自己的類型處理器來處理不支持的或非標(biāo)準的類型。 具體做法為:實現(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>

使用上述的類型處理器將會覆蓋已有的處理 Java ?String類型的屬性以及 ?VARCHAR ?類型的參數(shù)和結(jié)果的類型處理器。 要注意 MyBatis 不會通過檢測數(shù)據(jù)庫元信息來決定使用哪種類型,所以你必須在參數(shù)和結(jié)果映射中指明字段是 ?VARCHAR類型, 以使其能夠綁定到正確的類型處理器上。這是因為 MyBatis 直到語句被執(zhí)行時才清楚數(shù)據(jù)類型。

通過類型處理器的泛型,MyBatis 可以得知該類型處理器處理的 Java 類型,不過這種行為可以通過兩種方法改變:

  • 在類型處理器的配置元素(?typeHandler元素)上增加一個 ?javaType? 屬性(比如:?javaType="String"?);
  • 在類型處理器的類上增加一個 ?@MappedTypes? 注解指定與其關(guān)聯(lián)的 Java 類型列表。 如果在 ?javaType屬性中也同時指定,則注解上的配置將被忽略。

可以通過兩種方式來指定關(guān)聯(lián)的 ?JDBC類型:

  • 在類型處理器的配置元素上增加一個 ?jdbcType屬性(比如:?jdbcType="VARCHAR"?);
  • 在類型處理器的類上增加一個 ?@MappedJdbcTypes? 注解指定與其關(guān)聯(lián)的 ?JDBC類型列表。 如果在 ?jdbcType屬性中也同時指定,則注解上的配置將被忽略。

當(dāng)在 ?ResultMap中決定使用哪種類型處理器時,此時 Java 類型是已知的(從結(jié)果類型中獲得),但是 ?JDBC類型是未知的。 因此 Mybatis 使用?javaType=[Java 類型], jdbcType=null? 的組合來選擇一個類型處理器。 這意味著使用 ?@MappedJdbcTypes? 注解可以限制類型處理器的作用范圍,并且可以確保,除非顯式地設(shè)置,否則類型處理器在 ?ResultMap中將不會生效。 如果希望能在 ?ResultMap中隱式地使用類型處理器,那么設(shè)置 ?@MappedJdbcTypes? 注解的 ?includeNullJdbcType=true? 即可。 然而從 Mybatis 3.4.0 開始,如果某個 Java 類型只有一個注冊的類型處理器,即使沒有設(shè)置 ?includeNullJdbcType=true?,那么這個類型處理器也會是 ?ResultMap使用 Java 類型時的默認處理器。

最后,可以讓 MyBatis 幫你查找類型處理器:

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

注意在使用自動發(fā)現(xiàn)功能的時候,只能通過注解方式來指定JDBC的類型。

你可以創(chuàng)建能夠處理多個類的泛型類型處理器。為了使用泛型類型處理器, 需要增加一個接受該類的 class 作為參數(shù)的構(gòu)造器,這樣 MyBatis 會在構(gòu)造一個類型處理器實例的時候傳入一個具體的類。

//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和 ?EnumOrdinalTypeHandler都是泛型類型處理器,我們將會在接下來的部分詳細探討。

處理枚舉類型

若想映射枚舉類型 ?Enum?,則需要從 ?EnumTypeHandler或者 ?EnumOrdinalTypeHandler中選擇一個來使用。

比如說我們想存儲取近似值時用到的舍入模式。默認情況下,MyBatis 會利用 ?EnumTypeHandler來把 ?Enum值轉(zhuǎn)換成對應(yīng)的名字。

注意 ?EnumTypeHandler在某種意義上來說是比較特別的,其它的處理器只針對某個特定的類,而它不同,它會處理任意繼承了Enum的類。

不過,我們可能不想存儲名字,相反我們的DBA會堅持使用整形值代碼。那也一樣簡單:在配置文件中把EnumOrdinalTypeHandler加到typeHandlers中即可, 這樣每個 ?RoundingMode將通過他們的序數(shù)值來映射成對應(yīng)的整形數(shù)值。

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

但要是你想在一個地方將Enum映射成字符串,在另外一個地方映射成整形值呢?

自動映射器(?auto-mapper?)會自動地選用 ?EnumOrdinalTypeHandler來處理枚舉類型, 所以如果我們想用普通的 ?EnumTypeHandler?,就必須要顯式地為那些 SQL 語句設(shè)置要使用的類型處理器。

(下一節(jié)才開始介紹映射器文件,如果你是首次閱讀該文檔,你可能需要先跳過這里,過會再來看。)

<!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>

注意,這里的 ?select語句必須指定 ?resultMap而不是 ?resultType?。


以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號