Mybatis型別處理器--自定義typeHandler
Mybatis與資料庫互動時,需要對javaType和jdbcType進行相互轉換,為預處理語句設定引數時將javaType轉換為jdbcType,從結果集中獲取值時將jdbcType轉換為javaType。Mybatis已經為我們註冊了大部分基本型別的typeHandler,通常情況下,不需要我們自定義typeHandler。但有時為了方便,我們會選擇自定義typeHandler。
自定義型別處理器的步驟
自定義型別處理器typeHandler通常分為三步,詳細過程如下:
(1)建立一個自定義typeHandler的類;
(2)在Mybatis配置檔案中配置型別處理器,通過<typeHandlers></typeHandlers>進行配置;
(3)在引射器的XML配置中標識需要用自定義typeHandler處理的引數或者結果。
瞭解了自定義型別處理器大致步驟之後,我們接下來結合例子詳細介紹每一步需要完成的任務,這樣可以幫助我們更深刻理解型別處理器。我們可以通過兩種方式來建立自定義typeHandler類,一種是繼承:org.apache.ibatis.type.BaseTypeHandler,這是因為BaseTypeHandler已經實現了TypeHandler介面;另一種實現介面:org.apache.ibatis.type.TypeHandler。
通過繼承BaseTypeHandler建立typeHandler類
(1)建立自定義typeHandler類
首先,我們通過第二種方式,也即繼承BaseTypeHandler類,建立一個自定義typeHandler類(GenderTypeHandler),同樣對通過實現TypeHandler介面的方式進行介紹。為了方便介紹,我們假設需要處理的是性別,資料庫(Oracle)中的型別為NUMBER,值為1和2,分別表示男和女(性別的分類遠不止兩種,為了方便,多多理解),Java型別為String。GenderTypeHandler類程式碼如下:
@MappedTypes(Integer.class) @MappedJdbcTypes(JdbcType.NUMERIC) public class GenderTypeHandler 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 this.getGender(rs.getString(columnName)); } @Override public String getNullableResult(ResultSet rs, int columnIndex) throws SQLException { return this.getGender(rs.getString(columnIndex)); } @Override public String getNullableResult(CallableStatement cs, int columnIndex) throws SQLException { return this.getGender(cs.getString(columnIndex)); } private String getGender(String genderCode) { if("1".equals(genderCode)) { return "男"; }else if("2".equals(genderCode)) { return "女"; }else { return null; } } }
GenderTypeHandler類功能主要體現在兩方面,一方面使用預編譯(PreparedStatement)設定引數,另一個方面從結果集獲取對應的性別,以實現TypeHandler介面的方式建立自定型別處理器的功能也是如此。
需要注意,這裡我們使用了註解@MappedTypes和@MappedJdbcTypes,其中,@MappedTypes定義了javaType,指定攔截java型別,此外,我們也可以通過在Mybatis配置中typeHandler的javaType屬性定義;@MappedJdbcTypes定義了JdbcTypes,JdbcTypes需要從列舉類org.apache.ibatis.type.JdbcType中獲取,同樣,我們也可用配置檔案中typeHandler的jdbcType屬性定義。如果在配置檔案中使用typeHandler的屬性。註解@MappedTypes與@MappedJdbcTypes定義的會被忽略,也即屬性定義的優先順序高於註解定義的優先順序。
至此,我們可以進行測試,也即跳過第二步,直接進入第三步,在引射器的XML進行配置,程式碼片段如下:
<mapper namespace="cn.don.mapper.StudentMapper">
<resultMap type="cn.don.pojo.Student" id="queryStudentByNumResult">
<id property="studentNum" column="SNO"/>
<result property="name" column="SNAME"/>
<result property="age" column="SAGE"/>
<!-- 新增性別型別處理器 -->
<result property="gender" column="SSEX" typeHandler="cn.don.handler.typeHandler.GenderTypeHandler"/>
</resultMap>
<select id="queryStudentByNum" resultMap="queryStudentByNumResult">
SELECT T.SNO,
T.SNAME,
T.SAGE,
T.SSEX
FROM STUDENT T
WHERE T.SNO = #{studentNum}
</select>
</mapper>
這裡我們執行測試方法輸出一下測試結果:
如上,在result中使用typeHandler屬性指定型別處理器,建立型別處理器時可以不用新增MappedTypes和MappedJdbcTypes註解。小插曲過後,接下來我們介紹自定義型別處理器的第二步。
(2)在Mybatis配置檔案中配置型別處理器
上文我們已經詳細介紹了利用註解定義與利用typeHandler屬性定義javaType和jdbcType的區別,這裡不再贅述。配置typeHandlers時,需要按照Mybatis配置檔案指定的標籤順序配置,不然DTD會報錯。直接上程式碼了:
(3)在引射器的XML配置中標識需要用自定義typeHandler處理的引數或者結果
可粗略的分為兩種,一種直接在result標籤配置typeHandler屬性;另一種是配置result標籤javaType和jdbcType屬性,這種方式需要利用註解或者利用typeHandler屬性定義了javaType和jdbcType。利用result標籤指定javaType和jdbcType時,result標籤的屬性javaType和jdbcType的屬性值需要與之前定義好的一致,這樣Mybatis就會利用自定義的型別處理器為預編譯設定引數與獲取值了。
<mapper namespace="cn.don.mapper.StudentMapper">
<resultMap type="cn.don.pojo.Student" id="queryStudentByNumResult">
<id property="studentNum" column="SNO"/>
<result property="name" column="SNAME"/>
<result property="age" column="SAGE"/>
<!-- 新增性別型別處理器 -->
<result property="gender" column="SSEX" javaType="Integer" jdbcType="NUMERIC"/>
</resultMap>
<select id="queryStudentByNum" resultMap="queryStudentByNumResult">
SELECT T.SNO,
T.SNAME,
T.SAGE,
T.SSEX
FROM STUDENT T
WHERE T.SNO = #{studentNum}
</select>
</mapper>
通過繼承BaseTypeHandler類自定義型別處理器及其過程步驟就介紹完成了,接下來我們介紹通過實現TypeHandler介面的方式自定義型別處理器。
通過實現TypeHandler介面建立typeHandler類
這裡我們就不按照上文提到的步驟一步一步詳細介紹了,直接從程式碼開始。首先我們先建立一個列舉型別Gender,並在pojo型別中新增成員變數Gender,Gender程式碼(省略get、set方法)如下:
public enum Gender {
MALE(1,"男"),
FEMALE(2,"女");
private Integer code;
private String gender;
private Gender(Integer code,String gender) {
this.code = code;
this.gender = gender;
}
public static Gender getDender(Integer code) {
if(1 == code) {
return MALE;
}else if(2 == code) {
return FEMALE;
}else {
return null;
}
}
}
接下來我們建立SexTypeHandler實現TypeHandler介面,並完成實現方法,程式碼如下:
@MappedTypes(Gender.class)
@MappedJdbcTypes(JdbcType.NUMERIC)
public class SexTypeHandler implements TypeHandler<Gender>{
@Override
public void setParameter(PreparedStatement ps, int i, Gender parameter, JdbcType jdbcType) throws SQLException {
ps.setInt(i, parameter.getCode());
}
@Override
public Gender getResult(ResultSet rs, String columnName) throws SQLException {
return Gender.getDender(rs.getInt(columnName));
}
@Override
public Gender getResult(ResultSet rs, int columnIndex) throws SQLException {
return Gender.getDender(rs.getInt(columnIndex));
}
@Override
public Gender getResult(CallableStatement cs, int columnIndex) throws SQLException {
return Gender.getDender(cs.getInt(columnIndex));
}
}
最後我們在mybatis中新增配置好SexTypeHandler,並在引射器XML配置檔案中指定型別javaType和jdbcType,mybatis配置如下:
對映器配置如下:
<mapper namespace="cn.don.mapper.StudentMapper">
<resultMap type="cn.don.pojo.Student" id="queryStudentByNumResult">
<id property="studentNum" column="SNO"/>
<result property="name" column="SNAME"/>
<result property="age" column="SAGE"/>
<!-- 新增性別型別處理器 -->
<result property="sex" column="SSEX" javaType="cn.don.enumtype.Gender" jdbcType="NUMERIC" />
</resultMap>
<select id="queryStudentByNum" resultMap="queryStudentByNumResult">
SELECT T.SNO,
T.SNAME,
T.SAGE,
T.SSEX
FROM STUDENT T
WHERE T.SNO = #{studentNum}
</select>
</mapper>
到這兒就可以進行測試了,咋們先來看看測試結果:
通過實現介面的方式建立型別處理器就完成了。
此外,自定義型別處理器還可以建立處理多個類通用的typeHandler,Mybatis內建的有EnumTypeHandler和EnumOrdinalTypeHandler,前者使用列舉字串的名稱作為引數傳遞,而後者使用下標作為引數傳遞。建立處理多個類通用的typeHandler方法需要繼承BaseTypeHandler類,並新增一個以類為引數構造器以便Mybatis構造typeHandler時傳遞實際的類。程式碼如下:
public class GenericTypeHandler<E extends Student> extends BaseTypeHandler<E> {
private final Class<E> type;
public GenericTypeHandler(Class<E> type) {
if (type == null) {
throw new IllegalArgumentException("type argument cannot be null");
}
this.type = type;
}
// 省略子類需要實現的方法,方法實現與之前的一樣
...
}
總結
實際開發中,我們可能會用到自定義型別處理器,通過實現介面和繼承類兩種方式來完成自定義型別處理器,兩種方式無本質上的差別,使用哪一種均可。