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时传递实际的类。代码(这里泛型继承Student,这在实际中是不合适的)如下:

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;
	}
	// 省略子类需要实现的方法,方法实现与之前的一样
        ...
}

总结

实际开发中,我们可能会用到自定义类型处理器,通过实现接口和继承类两种方式来完成自定义类型处理器,两种方式无本质上的差别,使用哪一种均可。

 

猜你喜欢

转载自blog.csdn.net/qq_37134175/article/details/83753392