MyBatis typeHandlers 类型处理器
本文修订于:2022年6月11日
MyBatis typeHandlers 类型处理器
无论是 MyBatis在预处理语句(PreparedStatement)中设置一个参数时,还是从结果集中取出一个值时,都会用类型处理器将获取的值以合适的方式转换成Java类型。下表描述了一些默认的类型处理器:
类型处理器 | Java 类型 | JDBC 类型 |
BooleanTypeHandler | java.lang.Boolean, boolean | 数据库兼容的 BOOLEAN |
ByteTypeHandler | java.lang.Byte, byte | 数据库兼容的 NUMERIC 或 BYTE |
ShortTypeHandler | java.lang.Short, short | 数据库兼容的 NUMERIC 或 SHORT INTEGER |
IntegerTypeHandler | java.lang.Integer, int | 数据库兼容的 NUMERIC 或 INTEGER |
LongTypeHandler | java.lang.Long, long | 数据库兼容的 NUMERIC 或 LONG INTEGER |
FloatTypeHandler | java.lang.Float, float | 数据库兼容的 NUMERIC 或 FLOAT |
DoubleTypeHandler | java.lang.Double, double | 数据库兼容的 NUMERIC 或 DOUBLE |
BigDecimalTypeHandler | java.math.BigDecimal | 数据库兼容的 NUMERIC 或 DECIMAL |
StringTypeHandler | java.lang.String | CHAR, VARCHAR |
ClobTypeHandler | java.lang.String | CLOB, LONGVARCHAR |
NStringTypeHandler | java.lang.String | NVARCHAR, NCHAR |
NClobTypeHandler | java.lang.String | NCLOB |
ByteArrayTypeHandler | byte[] | 数据库兼容的字节流类型 |
BlobTypeHandler | byte[] | BLOB, LONGVARBINARY |
DateTypeHandler | java.util.Date | TIMESTAMP |
DateOnlyTypeHandler | java.util.Date | DATE |
TimeOnlyTypeHandler | java.util.Date | TIME |
SqlTimestampTypeHandler | java.sql.Timestamp | TIMESTAMP |
SqlDateTypeHandler | java.sql.Date | DATE |
SqlTimeTypeHandler | java.sql.Time | TIME |
ObjectTypeHandler | Any | OTHER 或未指定类型 |
EnumTypeHandler | Enumeration Type | VARCHAR-任何兼容的字符串类型,存储枚举的名称(而不是索引) |
EnumOrdinalTypeHandler | Enumeration Type | 任何兼容的 NUMERIC 或 DOUBLE 类型,存储枚举的索引(而不是名称)。 |
自定义类型处理器
可以重写类型处理器或创建自己的类型处理器来处理不支持的或非标准的类型。具体的做法为实现org.apache.ibatis.type.TypeHandler
接口或继承org.apache.ibatis.type.BaseTypeHandler
类,然后可以选择性地将它映射到一个JDBC类型。比如:
//ExampleTypeHandler.java
@MappedJdbcTypes(JdbcType.VARCHAR)
public class ExampleTypeHandler extends BaseTypeHandler<String>
{
public void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType) throws SQLException
{
ps.setString(i,parameter);
}
public String getNullableResult(ResultSet rs, String columnName) throws SQLException
{
return rs.getString(columnName);
}
public String getNullableResult(ResultSet rs, int columnIndex) throws SQLException
{
return rs.getString(columnIndex);
}
public String getNullableResult(CallableStatement cs, int columnIndex) throws SQLException
{
return cs.getString(columnIndex);
}
}
并且还需要在配置文件里面加上:
<!-- mybatis-config.xml -->
<typeHandlers>
<typeHandler handler="org.mybatis.example.ExampleTypeHandler"/>
</typeHandlers>
使用这个的类型处理器将会覆盖已经存在的处理Java的String类型属性和VARCHAR参数及结果的类型处理器。要注意MyBatis不会窥探数据库元信息来决定使用哪种类型,所以必须在参数和结果映射中指明是VARCHAR类型字段,以使其能绑定到正确的类型处理器上。这是因为MyBatis直到语句被执行才清楚数据类型。
通过类型处理器的泛型,MyBatis可以得知该类型处理器的Java类型,不过这种行为可以通过两种方法改变:
- 在类型处理器的配置元素(typeHandler element)上增加一个javaType属性。比如,javaType="String"
- 在类型处理器的类上(TypeHandler class)增加一个@MappedTypes注解来指定与其关联的Java类型列表。如果在javaType属性中也设置了,则注解方式将被忽略。
可以通过两种方式来指定被关联的JDBC类型:
- 在类型处理器的配置元素上增加一个javaType属性。比如:javaType="VARCHAR"
- 在类型处理器的类上(TypeHandler class)增加一个@MappedJdbcTypes注解来指定与其关联的JDBC类型列表。如果在javaType属性中也同时指定,则注解方式将被忽略。
最后,还可以让MyBatis查找类型处理器:
<!-- mybatis-config.xml -->
<typeHandlers>
<package name="org.mybatis.example"/>
</typeHandlers>
注意在使用自动检索(autodiscovery)功能的时候,只能通过注解的方式来指定JDBC类型。
你能创建一个泛型类型处理器,它可以处理多于一个类。为达到此目的,需要增加一个接收该类作为参数的构造器,这样在构造一个类型处理器的时候MyBatis就会传入一个具体的类。
//GenericTypeHandler.java
public class GenericTypeHandler<E extends MyObject> extends BaseTypeHandler<E>
{
private Class<E> type;
public GenericTypeHandler(Class<E> type)
{
if (type == null)
throw new IllegalArgumentException("Type argument cannot be null");
this.type = type;
}
...
}
处理枚举类型
EnumTypeHandler和EnumOrdinalTypeHandler都是泛型处理器(generic TypeHandlers),接下来的部分详细探讨。
若想映射枚举类型Enum,则需要从EnumTypeHandler或者EnumOrdinalTypeHandler中选一个来使用。比如说我们想存储近似值时用到的舍入模式。默认情况下,MyBatis会利用EnumTypeHandler来把Enum值转换成对应的名字。注意EnumTypeHandler在某种意义上来说是比较特别的,其他的处理器只针对某个特定的类,而它不同,它会处理任意继承了Enum的类。不过,我们可能不想存储名字,相反我们的DBA会坚持使用整型值代码。
在配置文件中把EnumOrdinalTypeHandler加到typeHandlers中即可,这样每个RoundingMode将通过他们的序数值来映射成对应的整型。
<!-- mybatis-config.xml -->
<typeHandlers>
<typeHandler handler="org.apache.ibatis.type.EnumOrdinalTypeHandler" javaType="java.math.RoundingMode"/>
</typeHandlers>
但是怎么样能将同样的Enum既映射成字符串又映射成整型呢?
自动映射器(auto-mapper)会自动选用EnumOrdinalTypeHandler来处理,所以如果我们想用普通的EnumTypeHandler,就非要为那些SQL语句显示地设置要用到的类型处理器不可。
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.apache.ibatis.submitted.rounding.Mapper">
<resultMap type="org.apache.ibatis.submitted.rounding.User" id="usermap">
<id column="id" property="id"/>
<result column="name" property="name"/>
<result column="funkyNumber" property="funkyNumber"/>
<result column="roundingMode" property="roundingMode"/>
</resultMap>
<select id="getUser" resultMap="usermap">
select * from users
</select>
<insert id="insert">
insert into users (id, name, funkyNumber, roundingMode) values
( #{id}, #{name}, #{funkyNumber},
#{roundingMode})
</insert>
<resultMap type="org.apache.ibatis.submitted.rounding.User" id="usermap2">
<id column="id" property="id"/>
<result column="name" property="name"/>
<result column="funkyNumber" property="funkyNumber"/>
<result column="roundingMode" property="roundingMode" typeHandler="org.apache.ibatis.type.EnumTypeHandler"/>
</resultMap>
<select id="getUser2" resultMap="usermap2">
select * from users2
</select>
<insert id="insert2">
insert into users2 (id, name, funkyNumber, roundingMode) values(
#{id}, #{name}, #{funkyNumber}, #{roundingMode, typeHandler=
org.apache.ibatis.type.EnumTypeHandler})
</insert>
</mapper>
注意,这里的select语句强制使用resultMap来代替resultType。