1. 程式人生 > >Mybatis型別處理器--自定義typeHandler

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;
	}
	// 省略子類需要實現的方法,方法實現與之前的一樣
        ...
}

總結

實際開發中,我們可能會用到自定義型別處理器,通過實現介面和繼承類兩種方式來完成自定義型別處理器,兩種方式無本質上的差別,使用哪一種均可。