MyBatis 在設(shè)置預(yù)處理語句(?PreparedStatement
?)中的參數(shù)或從結(jié)果集中取出一個值時, 都會用類型處理器將獲取到的值以合適的方式轉(zhuǎn)換成 Java 類型。下表描述了一些默認的類型處理器。
從 3.4.5 開始,MyBatis 默認支持 JSR-310(日期和時間 API) 。
類型處理器 | Java 類型 | JDBC 類型 |
---|---|---|
BooleanTypeHandler
|
java.lang.Boolean , boolean
|
數(shù)據(jù)庫兼容的 BOOLEAN
|
ByteTypeHandler
|
java.lang.Byte , byte
|
數(shù)據(jù)庫兼容的 NUMERIC 或 BYTE
|
ShortTypeHandler
|
java.lang.Short , short
|
數(shù)據(jù)庫兼容的 NUMERIC 或 SMALLINT
|
IntegerTypeHandler
|
java.lang.Integer , int
|
數(shù)據(jù)庫兼容的 NUMERIC 或 INTEGER
|
LongTypeHandler
|
java.lang.Long , long
|
數(shù)據(jù)庫兼容的 NUMERIC 或 BIGINT
|
FloatTypeHandler
|
java.lang.Float , float
|
數(shù)據(jù)庫兼容的 NUMERIC 或 FLOAT
|
DoubleTypeHandler
|
java.lang.Double , double
|
數(shù)據(jù)庫兼容的 NUMERIC 或 DOUBLE
|
BigDecimalTypeHandler
|
java.math.BigDecimal
|
數(shù)據(jù)庫兼容的 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ù)庫兼容的字節(jié)流類型 |
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 或未指定類型 |
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
?。
更多建議: