一、基本使用
Mybatis中的TypeHandler有两个功能,一个是实现javaType到jdbcType的转换,另外一个是实现jdbcType到javaType的转换。
TypeHandler是一个接口,其中重点是四个方法,一个取值的方法,三个设置值的方法:
/**
* @author Clinton Begin
*/
public interface TypeHandler<T> {
void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException;
T getResult(ResultSet rs, String columnName) throws SQLException;
T getResult(ResultSet rs, int columnIndex) throws SQLException;
T getResult(CallableStatement cs, int columnIndex) throws SQLException;
}
执行的流程如该图所示,设置参数可以将java类型转换为jdbc类型;取值方法可以将jdbc类型转换为java类型,而这个TypeHandler扮演了中间转换的角色。
在mybatis中有一个类型处理器的注册工厂,该工厂维护的是一个Map,来维护Java数据类型和类型处理器的对应关系。
Map<JdbcType, TypeHandler<?>> JDBC_TYPE_HANDLER_MAP
Mybatis内置了绝大多数的类型转换器,当出现复杂类型的映射时,才需要专门去定义自定义的类型转换器。
二、自定义类型转换器
我们在写mapper映射器的配置文件时,不经意间已经用到类型转换,不过是mybatis帮我们完成的。
<mapper namespace="com.hand.dao.UserMapper">
<insert id="insertUser" parameterType="com.hand.dto.User">
insert into tb_user (name,address,creation_date,last_update_date)
values
(#{name},#{address},#{creationDate},#{lastUpdateDate})
</insert>
</mapper>
像上面例子,只需要向update方法传入一个user对象,mybatis利用反射拆开user对象,然后根据对象中的字段在预处理语句(PreparedStatement)中设置参数,并且根据字段的类型,使用setXXX()方式设置相应的值。XXX可以是Integer,String,Date等Java类型。 同理,在从结果集(ResultSet)中取出一个值时,将使用rs.getInt、rs.getString、rs.getTimeStamp等方法将数据转换为Java对象。
那么问题来了,javaType和jdbcType的转换关系由谁来定呢?这就是类型处理器(type handlers)的功能所在。
比如java.lang.String转成JDBC.Varchar,java.lang.Integer转成JDBC.int。MyBatis使用内建的类型处理器能转换所有的基本数据类型、基本类型的包装类型、byte[] 、java.util.Date、java.sql.Date、java,sql.Time、java.sql.Timestamp、java枚举类型等。
不过对于自定义的类型怎么办呢?
假设上面的address在数据库字段类型是varchar(50),但是在User类中的address字段并不是String类型,而是一个自定义的Address
类型:
public class Address {
private String province;//省份
private String city;//城市
public String getProvince() {
return province;
}
public void setProvince(String province) {
this.province = province;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
}
此时,MyBatis并不知道该怎样来处理这个类型的对象。 因此,需要创建一个自定义的类型处理器(TypeHandler)了。
1、创建类型转换器:
/**
* @author [email protected]
* @version 1.0
* @name
* @description
* @date 2018/8/19
*/
public class AddressTypeHandler extends BaseTypeHandler<Address> {
@Override
public void setNonNullParameter(PreparedStatement preparedStatement, int i, Address address, JdbcType jdbcType) throws SQLException {
preparedStatement.setString(i, address.toString());
}
@Override
public Address getNullableResult(ResultSet resultSet, String s) throws SQLException {
return new Address(resultSet.getString(s));
}
@Override
public Address getNullableResult(ResultSet resultSet, int i) throws SQLException {
return new Address(resultSet.getString(i));
}
@Override
public Address getNullableResult(CallableStatement callableStatement, int i) throws SQLException {
return new Address(callableStatement.getString(i));
}
}
2、给Address创建构造函数,生成对象
//假设我们存储在db中的字符串是以","号分隔省市关系的
public Address(String address) {
if (address != null) {
String[] segments = address.split(",");
if (segments.length > 1) {
this.province = segments[0];
this.city = segments[1];
}
else if (segments.length > 0) {
this.city = segments[0];
}
}
}
3、实现Address的set方法
set方法是用来将java类型转成数据库存储的类型。重写Address的toString()方法
@Override
public String toString() {
return this.province + "," + this.city;
}
4、在mybatis文件中注册该类
在放置typeHandlers的各个标签的位置时需要注意放置的顺序:
需要按照这个顺序来放置,否则会报错,因为在mybatis在启动时会根据这个顺序去加载各个节点标签的属性值到config对象中。
原博文参考地址:http://blog.csdn.net/soonfly/article/details/65628372