MyBatis 3手册 MyBatis 3 XML配置-类型处理器

2024-02-25 开发教程 MyBatis 3手册 匿名 4

MyBatis 在设置预处理语句(​PreparedStatement​)中的参数或从结果集中取出一个值时, 都会用类型处理器将获取到的值以合适的方式转换成 Java 类型。下表描述了一些默认的类型处理器。

从 3.4.5 开始,MyBatis 默认支持 JSR-310(日期和时间 API) 。

类型处理器Java 类型JDBC 类型
BooleanTypeHandlerjava.lang.Boolean, boolean数据库兼容的 BOOLEAN
ByteTypeHandlerjava.lang.Byte, byte数据库兼容的 NUMERICBYTE
ShortTypeHandlerjava.lang.Short, short数据库兼容的 NUMERICSMALLINT
IntegerTypeHandlerjava.lang.Integer, int数据库兼容的 NUMERICINTEGER
LongTypeHandlerjava.lang.Long, long数据库兼容的 NUMERICBIGINT
FloatTypeHandlerjava.lang.Float, float数据库兼容的 NUMERICFLOAT
DoubleTypeHandlerjava.lang.Double, double数据库兼容的 NUMERICDOUBLE
BigDecimalTypeHandlerjava.math.BigDecimal数据库兼容的 NUMERICDECIMAL
StringTypeHandlerjava.lang.StringCHAR, VARCHAR
ClobReaderTypeHandlerjava.io.Reader-
ClobTypeHandlerjava.lang.StringCLOB, LONGVARCHAR
NStringTypeHandlerjava.lang.StringNVARCHAR, NCHAR
NClobTypeHandlerjava.lang.StringNCLOB
BlobInputStreamTypeHandlerjava.io.InputStream-
ByteArrayTypeHandlerbyte[]数据库兼容的字节流类型
BlobTypeHandlerbyte[]BLOB, LONGVARBINARY
DateTypeHandlerjava.util.DateTIMESTAMP
DateOnlyTypeHandlerjava.util.DateDATE
TimeOnlyTypeHandlerjava.util.DateTIME
SqlTimestampTypeHandlerjava.sql.TimestampTIMESTAMP
SqlDateTypeHandlerjava.sql.DateDATE
SqlTimeTypeHandlerjava.sql.TimeTIME
ObjectTypeHandlerAnyOTHER或未指定类型
EnumTypeHandlerEnumeration TypeVARCHAR 或任何兼容的字符串类型,用来存储枚举的名称(而不是索引序数值)
EnumOrdinalTypeHandlerEnumeration Type任何兼容的 NUMERICDOUBLE类型,用来存储枚举的序数值(而不是名称)。
SqlxmlTypeHandlerjava.lang.StringSQLXML
InstantTypeHandlerjava.time.InstantTIMESTAMP
LocalDateTimeTypeHandlerjava.time.LocalDateTimeTIMESTAMP
LocalDateTypeHandlerjava.time.LocalDateDATE
LocalTimeTypeHandlerjava.time.LocalTimeTIME
OffsetDateTimeTypeHandlerjava.time.OffsetDateTimeTIMESTAMP
OffsetTimeTypeHandlerjava.time.OffsetTimeTIME
ZonedDateTimeTypeHandlerjava.time.ZonedDateTimeTIMESTAMP
YearTypeHandlerjava.time.YearINTEGER
MonthTypeHandlerjava.time.MonthINTEGER
YearMonthTypeHandlerjava.time.YearMonthVARCHARLONGVARCHAR
JapaneseDateTypeHandlerjava.time.chrono.JapaneseDateDATE

你可以重写已有的类型处理器或创建你自己的类型处理器来处理不支持的或非标准的类型。 具体做法为:实现 ​org.apache.ibatis.type.TypeHandler​ 接口, 或继承一个很便利的类 ​org.apache.ibatis.type.BaseTypeHandler​, 并且可以(可选地)将它映射到一个 ​JDBC类型。比如:

// ExampleTypeHandler.java
@MappedJdbcTypes(JdbcType.VARCHAR)
public class ExampleTypeHandler 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 rs.getString(columnName);
}
@Override
public String getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
return rs.getString(columnIndex);
}
@Override
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元素)上增加一个 ​javaType​ 属性(比如:​javaType="String"​);
  • 在类型处理器的类上增加一个 ​@MappedTypes​ 注解指定与其关联的 Java 类型列表。 如果在 ​javaType属性中也同时指定,则注解上的配置将被忽略。

可以通过两种方式来指定关联的 ​JDBC类型:

  • 在类型处理器的配置元素上增加一个 ​jdbcType属性(比如:​jdbcType="VARCHAR"​);
  • 在类型处理器的类上增加一个 ​@MappedJdbcTypes​ 注解指定与其关联的 ​JDBC类型列表。 如果在 ​jdbcType属性中也同时指定,则注解上的配置将被忽略。

当在 ​ResultMap中决定使用哪种类型处理器时,此时 Java 类型是已知的(从结果类型中获得),但是 ​JDBC类型是未知的。 因此 Mybatis 使用​javaType=[Java 类型], jdbcType=null​ 的组合来选择一个类型处理器。 这意味着使用 ​@MappedJdbcTypes​ 注解可以限制类型处理器的作用范围,并且可以确保,除非显式地设置,否则类型处理器在 ​ResultMap中将不会生效。 如果希望能在 ​ResultMap中隐式地使用类型处理器,那么设置 ​@MappedJdbcTypes​ 注解的 ​includeNullJdbcType=true​ 即可。 然而从 Mybatis 3.4.0 开始,如果某个 Java 类型只有一个注册的类型处理器,即使没有设置 ​includeNullJdbcType=true​,那么这个类型处理器也会是 ​ResultMap使用 Java 类型时的默认处理器。

最后,可以让 MyBatis 帮你查找类型处理器:

<!-- mybatis-config.xml -->
<typeHandlers>
<package name="org.mybatis.example"/>
</typeHandlers>

注意在使用自动发现功能的时候,只能通过注解方式来指定JDBC的类型。

你可以创建能够处理多个类的泛型类型处理器。为了使用泛型类型处理器, 需要增加一个接受该类的 class 作为参数的构造器,这样 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都是泛型类型处理器,我们将会在接下来的部分详细探讨。

处理枚举类型

若想映射枚举类型 ​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​。