MyBatis 在設(shè)置預(yù)處理語(yǔ)句(?PreparedStatement
?)中的參數(shù)或從結(jié)果集中取出一個(gè)值時(shí), 都會(huì)用類(lèi)型處理器將獲取到的值以合適的方式轉(zhuǎn)換成 Java 類(lèi)型。下表描述了一些默認(rèn)的類(lèi)型處理器。
從 3.4.5 開(kāi)始,MyBatis 默認(rèn)支持 JSR-310(日期和時(shí)間 API) 。
類(lèi)型處理器 | Java 類(lèi)型 | JDBC 類(lèi)型 |
---|---|---|
BooleanTypeHandler
|
java.lang.Boolean , boolean
|
數(shù)據(jù)庫(kù)兼容的 BOOLEAN
|
ByteTypeHandler
|
java.lang.Byte , byte
|
數(shù)據(jù)庫(kù)兼容的 NUMERIC 或 BYTE
|
ShortTypeHandler
|
java.lang.Short , short
|
數(shù)據(jù)庫(kù)兼容的 NUMERIC 或 SMALLINT
|
IntegerTypeHandler
|
java.lang.Integer , int
|
數(shù)據(jù)庫(kù)兼容的 NUMERIC 或 INTEGER
|
LongTypeHandler
|
java.lang.Long , long
|
數(shù)據(jù)庫(kù)兼容的 NUMERIC 或 BIGINT
|
FloatTypeHandler
|
java.lang.Float , float
|
數(shù)據(jù)庫(kù)兼容的 NUMERIC 或 FLOAT
|
DoubleTypeHandler
|
java.lang.Double , double
|
數(shù)據(jù)庫(kù)兼容的 NUMERIC 或 DOUBLE
|
BigDecimalTypeHandler
|
java.math.BigDecimal
|
數(shù)據(jù)庫(kù)兼容的 NUMERIC 或 DECIMAL
|
StringTypeHandler
|
java.lang.String
|
CHAR , VARCHAR
|
ClobReaderTypeHandler
|
java.io.Reader
|
- |
ClobTypeHandler
|
java.lang.String
|
CLOB , LONGVARCHAR
|
NStringTypeHandler
|
java.lang.String
|
NVARCHAR , NCHAR
|
NClobTypeHandler
|
java.lang.String
|
NCLOB
|
BlobInputStreamTypeHandler
|
java.io.InputStream
|
- |
ByteArrayTypeHandler
|
byte[]
|
數(shù)據(jù)庫(kù)兼容的字節(jié)流類(lèi)型 |
BlobTypeHandler
|
byte[]
|
BLOB , LONGVARBINARY
|
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 或未指定類(lèi)型 |
EnumTypeHandler
|
Enumeration Type | VARCHAR 或任何兼容的字符串類(lèi)型,用來(lái)存儲(chǔ)枚舉的名稱(chēng)(而不是索引序數(shù)值) |
EnumOrdinalTypeHandler
|
Enumeration Type | 任何兼容的 NUMERIC 或 DOUBLE 類(lèi)型,用來(lái)存儲(chǔ)枚舉的序數(shù)值(而不是名稱(chēng))。 |
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
|
你可以重寫(xiě)已有的類(lèi)型處理器或創(chuàng)建你自己的類(lèi)型處理器來(lái)處理不支持的或非標(biāo)準(zhǔn)的類(lèi)型。 具體做法為:實(shí)現(xiàn) ?org.apache.ibatis.type.TypeHandler
? 接口, 或繼承一個(gè)很便利的類(lèi) ?org.apache.ibatis.type.BaseTypeHandler
?, 并且可以(可選地)將它映射到一個(gè) ?JDBC
類(lèi)型。比如:
// 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>
使用上述的類(lèi)型處理器將會(huì)覆蓋已有的處理 Java ?String
類(lèi)型的屬性以及 ?VARCHAR
?類(lèi)型的參數(shù)和結(jié)果的類(lèi)型處理器。 要注意 MyBatis 不會(huì)通過(guò)檢測(cè)數(shù)據(jù)庫(kù)元信息來(lái)決定使用哪種類(lèi)型,所以你必須在參數(shù)和結(jié)果映射中指明字段是 ?VARCHAR
類(lèi)型, 以使其能夠綁定到正確的類(lèi)型處理器上。這是因?yàn)?nbsp;MyBatis 直到語(yǔ)句被執(zhí)行時(shí)才清楚數(shù)據(jù)類(lèi)型。
通過(guò)類(lèi)型處理器的泛型,MyBatis 可以得知該類(lèi)型處理器處理的 Java 類(lèi)型,不過(guò)這種行為可以通過(guò)兩種方法改變:
typeHandler
元素)上增加一個(gè) ?javaType
? 屬性(比如:?javaType="String"
?);
@MappedTypes
? 注解指定與其關(guān)聯(lián)的 Java 類(lèi)型列表。 如果在 ?javaType
屬性中也同時(shí)指定,則注解上的配置將被忽略。可以通過(guò)兩種方式來(lái)指定關(guān)聯(lián)的 ?JDBC
類(lèi)型:
jdbcType
屬性(比如:?jdbcType="VARCHAR"
?);
@MappedJdbcTypes
? 注解指定與其關(guān)聯(lián)的 ?JDBC
類(lèi)型列表。 如果在 ?jdbcType
屬性中也同時(shí)指定,則注解上的配置將被忽略。當(dāng)在 ?ResultMap
中決定使用哪種類(lèi)型處理器時(shí),此時(shí) Java 類(lèi)型是已知的(從結(jié)果類(lèi)型中獲得),但是 ?JDBC
類(lèi)型是未知的。 因此 Mybatis 使用?javaType=[Java 類(lèi)型], jdbcType=null
? 的組合來(lái)選擇一個(gè)類(lèi)型處理器。 這意味著使用 ?@MappedJdbcTypes
? 注解可以限制類(lèi)型處理器的作用范圍,并且可以確保,除非顯式地設(shè)置,否則類(lèi)型處理器在 ?ResultMap
中將不會(huì)生效。 如果希望能在 ?ResultMap
中隱式地使用類(lèi)型處理器,那么設(shè)置 ?@MappedJdbcTypes
? 注解的 ?includeNullJdbcType=true
? 即可。 然而從 Mybatis 3.4.0 開(kāi)始,如果某個(gè) Java 類(lèi)型只有一個(gè)注冊(cè)的類(lèi)型處理器,即使沒(méi)有設(shè)置 ?includeNullJdbcType=true
?,那么這個(gè)類(lèi)型處理器也會(huì)是 ?ResultMap
使用 Java 類(lèi)型時(shí)的默認(rèn)處理器。
最后,可以讓 MyBatis 幫你查找類(lèi)型處理器:
<!-- mybatis-config.xml -->
<typeHandlers>
<package name="org.mybatis.example"/>
</typeHandlers>
注意在使用自動(dòng)發(fā)現(xiàn)功能的時(shí)候,只能通過(guò)注解方式來(lái)指定JDBC
的類(lèi)型。
你可以創(chuàng)建能夠處理多個(gè)類(lèi)的泛型類(lèi)型處理器。為了使用泛型類(lèi)型處理器, 需要增加一個(gè)接受該類(lèi)的 class 作為參數(shù)的構(gòu)造器,這樣 MyBatis 會(huì)在構(gòu)造一個(gè)類(lèi)型處理器實(shí)例的時(shí)候傳入一個(gè)具體的類(lèi)。
//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
都是泛型類(lèi)型處理器,我們將會(huì)在接下來(lái)的部分詳細(xì)探討。
若想映射枚舉類(lèi)型 ?Enum
?,則需要從 ?EnumTypeHandler
或者 ?EnumOrdinalTypeHandler
中選擇一個(gè)來(lái)使用。
比如說(shuō)我們想存儲(chǔ)取近似值時(shí)用到的舍入模式。默認(rèn)情況下,MyBatis 會(huì)利用 ?EnumTypeHandler
來(lái)把 ?Enum
值轉(zhuǎn)換成對(duì)應(yīng)的名字。
注意 ?EnumTypeHandler
在某種意義上來(lái)說(shuō)是比較特別的,其它的處理器只針對(duì)某個(gè)特定的類(lèi),而它不同,它會(huì)處理任意繼承了Enum
的類(lèi)。
不過(guò),我們可能不想存儲(chǔ)名字,相反我們的DBA
會(huì)堅(jiān)持使用整形值代碼。那也一樣簡(jiǎn)單:在配置文件中把EnumOrdinalTypeHandler
加到typeHandlers
中即可, 這樣每個(gè) ?RoundingMode
將通過(guò)他們的序數(shù)值來(lái)映射成對(duì)應(yīng)的整形數(shù)值。
<!-- mybatis-config.xml -->
<typeHandlers>
<typeHandler handler="org.apache.ibatis.type.EnumOrdinalTypeHandler" javaType="java.math.RoundingMode"/>
</typeHandlers>
但要是你想在一個(gè)地方將Enum
映射成字符串,在另外一個(gè)地方映射成整形值呢?
自動(dòng)映射器(?auto-mapper
?)會(huì)自動(dòng)地選用 ?EnumOrdinalTypeHandler
來(lái)處理枚舉類(lèi)型, 所以如果我們想用普通的 ?EnumTypeHandler
?,就必須要顯式地為那些 SQL 語(yǔ)句設(shè)置要使用的類(lèi)型處理器。
(下一節(jié)才開(kāi)始介紹映射器文件,如果你是首次閱讀該文檔,你可能需要先跳過(guò)這里,過(guò)會(huì)再來(lái)看。)
<!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
語(yǔ)句必須指定 ?resultMap
而不是 ?resultType
?。
更多建議: