前言:直入正题,在我们利用mybatis作为持久层框架存储数据时,从mybatis接收参数到mysql存储数据,都会用到typeHandler类型处理器。这也就是从JavaType->JdbcType的转化过程。由于mybatis初始时已经内置大部分基础类型转化的TypeHandler,已经足够我们平常的简单应用开发了,所以大多数情况下并不需要我们自己去定义类型转换器。但是,当遇到一些特殊情况时,为了开发的方便性,我们才回去自定义一些类型转换器。
使用场景:mybatis在预处理语句(PreparedStatement)中设置一个参数时,或者从结果集(ResultSet)中取出一个值时,都会用到TypeHandler。它的作用就是将java类型(javaType)转化为jdbc类型(jdbcType),或者将jdbc类型(jdbcType)转化为java类型(javaType)。
mybatis初始内置TypeHandler:
public TypeHandlerRegistry() {
this.register((Class)Boolean.class, (TypeHandler)(new BooleanTypeHandler()));
this.register((Class)Boolean.TYPE, (TypeHandler)(new BooleanTypeHandler()));
this.register((JdbcType)JdbcType.BOOLEAN, (TypeHandler)(new BooleanTypeHandler()));
this.register((JdbcType)JdbcType.BIT, (TypeHandler)(new BooleanTypeHandler()));
this.register((Class)Byte.class, (TypeHandler)(new ByteTypeHandler()));
this.register((Class)Byte.TYPE, (TypeHandler)(new ByteTypeHandler()));
this.register((JdbcType)JdbcType.TINYINT, (TypeHandler)(new ByteTypeHandler()));
this.register((Class)Short.class, (TypeHandler)(new ShortTypeHandler()));
this.register((Class)Short.TYPE, (TypeHandler)(new ShortTypeHandler()));
this.register((JdbcType)JdbcType.SMALLINT, (TypeHandler)(new ShortTypeHandler()));
this.register((Class)Integer.class, (TypeHandler)(new IntegerTypeHandler()));
this.register((Class)Integer.TYPE, (TypeHandler)(new IntegerTypeHandler()));
this.register((JdbcType)JdbcType.INTEGER, (TypeHandler)(new IntegerTypeHandler()));
this.register((Class)Long.class, (TypeHandler)(new LongTypeHandler()));
this.register((Class)Long.TYPE, (TypeHandler)(new LongTypeHandler()));
this.register((Class)Float.class, (TypeHandler)(new FloatTypeHandler()));
this.register((Class)Float.TYPE, (TypeHandler)(new FloatTypeHandler()));
this.register((JdbcType)JdbcType.FLOAT, (TypeHandler)(new FloatTypeHandler()));
this.register((Class)Double.class, (TypeHandler)(new DoubleTypeHandler()));
this.register((Class)Double.TYPE, (TypeHandler)(new DoubleTypeHandler()));
this.register((JdbcType)JdbcType.DOUBLE, (TypeHandler)(new DoubleTypeHandler()));
this.register((Class)Reader.class, (TypeHandler)(new ClobReaderTypeHandler()));
this.register((Class)String.class, (TypeHandler)(new StringTypeHandler()));
this.register((Class)String.class, JdbcType.CHAR, (TypeHandler)(new StringTypeHandler()));
this.register((Class)String.class, JdbcType.CLOB, (TypeHandler)(new ClobTypeHandler()));
this.register((Class)String.class, JdbcType.VARCHAR, (TypeHandler)(new StringTypeHandler()));
this.register((Class)String.class, JdbcType.LONGVARCHAR, (TypeHandler)(new ClobTypeHandler()));
this.register((Class)String.class, JdbcType.NVARCHAR, (TypeHandler)(new NStringTypeHandler()));
this.register((Class)String.class, JdbcType.NCHAR, (TypeHandler)(new NStringTypeHandler()));
this.register((Class)String.class, JdbcType.NCLOB, (TypeHandler)(new NClobTypeHandler()));
this.register((JdbcType)JdbcType.CHAR, (TypeHandler)(new StringTypeHandler()));
this.register((JdbcType)JdbcType.VARCHAR, (TypeHandler)(new StringTypeHandler()));
this.register((JdbcType)JdbcType.CLOB, (TypeHandler)(new ClobTypeHandler()));
this.register((JdbcType)JdbcType.LONGVARCHAR, (TypeHandler)(new ClobTypeHandler()));
this.register((JdbcType)JdbcType.NVARCHAR, (TypeHandler)(new NStringTypeHandler()));
this.register((JdbcType)JdbcType.NCHAR, (TypeHandler)(new NStringTypeHandler()));
this.register((JdbcType)JdbcType.NCLOB, (TypeHandler)(new NClobTypeHandler()));
this.register((Class)Object.class, JdbcType.ARRAY, (TypeHandler)(new ArrayTypeHandler()));
this.register((JdbcType)JdbcType.ARRAY, (TypeHandler)(new ArrayTypeHandler()));
this.register((Class)BigInteger.class, (TypeHandler)(new BigIntegerTypeHandler()));
this.register((JdbcType)JdbcType.BIGINT, (TypeHandler)(new LongTypeHandler()));
this.register((Class)BigDecimal.class, (TypeHandler)(new BigDecimalTypeHandler()));
this.register((JdbcType)JdbcType.REAL, (TypeHandler)(new BigDecimalTypeHandler()));
this.register((JdbcType)JdbcType.DECIMAL, (TypeHandler)(new BigDecimalTypeHandler()));
this.register((JdbcType)JdbcType.NUMERIC, (TypeHandler)(new BigDecimalTypeHandler()));
this.register((Class)InputStream.class, (TypeHandler)(new BlobInputStreamTypeHandler()));
this.register((Class)Byte[].class, (TypeHandler)(new ByteObjectArrayTypeHandler()));
this.register((Class)Byte[].class, JdbcType.BLOB, (TypeHandler)(new BlobByteObjectArrayTypeHandler()));
this.register((Class)Byte[].class, JdbcType.LONGVARBINARY, (TypeHandler)(new BlobByteObjectArrayTypeHandler()));
this.register((Class)byte[].class, (TypeHandler)(new ByteArrayTypeHandler()));
this.register((Class)byte[].class, JdbcType.BLOB, (TypeHandler)(new BlobTypeHandler()));
this.register((Class)byte[].class, JdbcType.LONGVARBINARY, (TypeHandler)(new BlobTypeHandler()));
this.register((JdbcType)JdbcType.LONGVARBINARY, (TypeHandler)(new BlobTypeHandler()));
this.register((JdbcType)JdbcType.BLOB, (TypeHandler)(new BlobTypeHandler()));
this.register(Object.class, this.UNKNOWN_TYPE_HANDLER);
this.register(Object.class, JdbcType.OTHER, this.UNKNOWN_TYPE_HANDLER);
this.register(JdbcType.OTHER, this.UNKNOWN_TYPE_HANDLER);
this.register((Class)Date.class, (TypeHandler)(new DateTypeHandler()));
this.register((Class)Date.class, JdbcType.DATE, (TypeHandler)(new DateOnlyTypeHandler()));
this.register((Class)Date.class, JdbcType.TIME, (TypeHandler)(new TimeOnlyTypeHandler()));
this.register((JdbcType)JdbcType.TIMESTAMP, (TypeHandler)(new DateTypeHandler()));
this.register((JdbcType)JdbcType.DATE, (TypeHandler)(new DateOnlyTypeHandler()));
this.register((JdbcType)JdbcType.TIME, (TypeHandler)(new TimeOnlyTypeHandler()));
this.register((Class)java.sql.Date.class, (TypeHandler)(new SqlDateTypeHandler()));
this.register((Class)Time.class, (TypeHandler)(new SqlTimeTypeHandler()));
this.register((Class)Timestamp.class, (TypeHandler)(new SqlTimestampTypeHandler()));
上面都是mybatis内置的处理器,所以在平时开发的时候我们不用去关心java到数据库的类型转化关系,mybatis都帮我们把这些工作完成了。但这并不是我们要关心的重点,我们需要的是自定义TypeHandler去应对更多的需求。
对TypeHandler接口的一些说明:
TypeHandler是一个接口,它定义了如下四个方法,实现类必须去实现,方法如下:
void setParameter(PreparedStatement var1, int var2, T var3, JdbcType var4) throws SQLException;
T getResult(ResultSet var1, String var2) throws SQLException;
T getResult(ResultSet var1, int var2) throws SQLException;
T getResult(CallableStatement var1, int var2) throws SQLException;
}
setParameter:通过preparedStatement对象设置参数,将T类型的数据存入数据库。
getResult:通过列名或者下标来获取结果数据,也可以通过CallableStatement获取数据。
自定义注解里还可以通过注解配置java类型和jdbc类型:
@MappedTypes() :注解配置java类型
@MappedJdbcTypes(): 注解配置jdbc类型
枚举类型的TypeHandler:
EnumOrdinalTypeHandler:使用枚举字符串名称作为参数传递。
EnumTypeHandler:使用整数下标作为参数传递。
为了实现自定义枚举类型的TypeHandler,我们通过性别(Sex)类型来进行演示:
首先定义Sex类型的枚举,定义如下:
package com.cbg.Entity;
/**
* Created by chenboge on 2017/5/18.
*
* Email:[email protected]
*
* description:
*/
//用于SexTypeHandler的性别转换器枚举
public enum Sex {
//每一个类型都是一个枚举类(Sex)的实例
MALE(0, "男"),
FMALE(1, "女");
//用于保存在数据库
private int SexCode;
//用于UI展示
private String SexName;
Sex(int sexCode, String sexName) {
SexCode = sexCode;
SexName = sexName;
}
public int getSexCode() {
return SexCode;
}
//通过SexCode的值来获取Sex枚举类型,数据库只需保存code,通过代码解析成Sex类型
public static Sex getSexFromCode(int code) {
for (Sex sex : Sex.values()) {
if (sex.getSexCode() == code) {
return sex;
}
}
return null;
}
}
如果我们不自定义TypeHandler,也可以通过mybatis内置的EnumOrdinalTypeHandler来进行类型转换,在mapper文件中的resultmap中进行如下配置:
<result property="sex" column="sex" typeHandler="org.apache.ibatis.type.EnumOrdinalTypeHandler"/>
自定义枚举TypeHandler:
package com.cbg.handler;
import com.cbg.Entity.Sex;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.TypeHandler;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
/**
* Created by chenboge on 2017/5/18.
*
* Email:baigegechen@gmail.com
*
* description:
*/
public class SexTypeHandler implements TypeHandler<Sex> {
@Override
public void setParameter(PreparedStatement preparedStatement, int i, Sex sex, JdbcType jdbcType) throws SQLException {
//设置第i个参数的值为传入sex的code值,preparedStatement为执行数据库操纵的对象
//传值的时候是一个sex对象,但是当进行映射插入的时候就会转化为sex的code值进行存储
preparedStatement.setInt(i, sex.getSexCode());
}
@Override
public Sex getResult(ResultSet resultSet, String s) throws SQLException {
//获取数据库存储的sex的code值
int result = resultSet.getInt(s);
return Sex.getSexFromCode(result);
}
@Override
public Sex getResult(ResultSet resultSet, int i) throws SQLException {
int result = resultSet.getInt(i);
return Sex.getSexFromCode(result);
}
@Override
public Sex getResult(CallableStatement callableStatement, int i) throws SQLException {
int result = callableStatement.getInt(i);
return Sex.getSexFromCode(result);
}
}
最后需要在配置文件中运用我们自定义的TypeHandler对Sex进行类型转换:
id="userMap" type="com.cbg.pojo.UserBean">
<result property="id" column="id"/>
<result property="userName" column="user_name"/>
<result property="birthday" column="birthday"/>
<result property="mobile" column="mobile"/>
<result property="email" column="email"/>
<result property="note" column="note"/>
<result property="sex" column="sex" javaType="com.cbg.Entity.Sex" jdbcType="INTEGER"
typeHandler="com.cbg.handler.SexTypeHandler"/>
通过上面的配置,自定义的TypeHandler就会生效,对java中的Sex对象和数据库中的Integer进行自定转换。数据库只需存储Sex的code值,获取数据库数据时自动转化为Sex枚举对象,使用相当方便,也更加灵活。
总结:自定义TypeHandler总体上来说还是比较简单的。还有不清楚的同学可以移步:ssm基础框架整合+自定义枚举TypeHandler进行查看。