一、什么是类型处理器
1、类型处理器(TypeHandler)
MyBatis 在设置预处理语句(PreparedStatement)中的参数或从结果集中取出一个值时,都会用类型处理器将获取到的值以合适的方式转换成 Java 类型。也就是java类型与jdbc类型之间的处换器,诸如最常见StringTypeHandler。
2、Mybatis内置了哪些类型处理器
如下图,类型处理器的顶级接口是org.apache.ibatis.type.TypeHandler。
下表描述了一些默认的类型处理器,官方原表。
类型处理器 | Java 类型 | JDBC 类型 |
---|---|---|
BooleanTypeHandler |
java.lang.Boolean , boolean |
数据库兼容的 BOOLEAN |
ByteTypeHandler |
java.lang.Byte , byte |
数据库兼容的 NUMERIC 或 BYTE |
ShortTypeHandler |
java.lang.Short , short |
数据库兼容的 NUMERIC 或 SMALLINT |
IntegerTypeHandler |
java.lang.Integer , int |
数据库兼容的 NUMERIC 或 INTEGER |
LongTypeHandler |
java.lang.Long , long |
数据库兼容的 NUMERIC 或 BIGINT |
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 |
ClobReaderTypeHandler |
java.io.Reader |
- |
ClobTypeHandler |
java.lang.String |
CLOB , LONGVARCHAR |
NStringTypeHandler |
java.lang.String |
NVARCHAR , NCHAR |
NClobTypeHandler |
java.lang.String |
NCLOB |
BlobInputStreamTypeHandler |
java.io.InputStream |
- |
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 类型,用来存储枚举的序数值(而不是名称)。 |
SqlxmlTypeHandler |
java.lang.String |
SQLXML |
InstantTypeHandler |
java.time.Instant |
TIMESTAMP |
LocalDateTimeTypeHandler |
java.time.LocalDateTime |
TIMESTAMP |
LocalDateTypeHandler |
java.time.LocalDate |
DATE |
LocalTimeTypeHandler |
java.time.LocalTime |
TIME |
OffsetDateTimeTypeHandler |
java.time.OffsetDateTime |
TIMESTAMP |
OffsetTimeTypeHandler |
java.time.OffsetTime |
TIME |
ZonedDateTimeTypeHandler |
java.time.ZonedDateTime |
TIMESTAMP |
YearTypeHandler |
java.time.Year |
INTEGER |
MonthTypeHandler |
java.time.Month |
INTEGER |
YearMonthTypeHandler |
java.time.YearMonth |
VARCHAR 或 LONGVARCHAR |
JapaneseDateTypeHandler |
java.time.chrono.JapaneseDate |
DATE |
二、如何自定义类型处理器
尽管mybatis提供了许多类型处理器,但有时由于业务的需要,需要自定义类型处理器。例如,java实体类中Long[]数组类型如何以JSON字符串格式存储到数据库中varchar中,又如何能正常查询出,此时就需要自定义类型了。
1、自定义处理器
实现 org.apache.ibatis.type.TypeHandler 接口, 或继承一个很便利的类org.apache.ibatis.type.BaseTypeHandler, 并且可以(可选地)将它映射到一个 JDBC 类型。
@MappedJdbcTypes(JdbcType.VARCHAR) @MappedTypes(String.class) public class MyTypeHandler extends BaseTypeHandler<String> { /* * 加工入参 */ @Override public void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType) throws SQLException { if (parameter != null) { //加工 ps.setString(i, parameter + "TEST"); } } /* * 根据列名获取返回结果,可在此方法中加工返回值 */ @Override public String getNullableResult(ResultSet rs, String columnName) throws SQLException { String originRes = rs.getString(columnName); if (originRes != null) { log.info("原对象为--> {}", originRes); return originRes + "TEST"; }return null; } /* * 根据列下标获取返回结果,可在此方法中加工返回值 */ @Override public String getNullableResult(ResultSet rs, int columnIndex) throws SQLException { String originRes = rs.getString(columnIndex); if (originRes != null) { log.info("原对象为--> {}", originRes);return originRes+ "TEST"; }return null; } /* * 根据列下标获取返回结果(存储过程),可在此方法中加工返回值 */ @Override public String getNullableResult(CallableStatement cs, int columnIndex) throws SQLException { String originRes = rs.getString(columnIndex);
if (originRes != null) {
log.info("原对象为--> {}", originRes);
return originRes+ "TEST";
}return null; } }
是否要指定javaType与jdbcType?
官方解释:要注意 MyBatis 不会通过检测数据库元信息来决定使用哪种类型,所以你必须在参数和结果映射中指明字段的jdbcType,以使其能够绑定到正确的类型处理器上。这是因为 MyBatis 直到语句被执行时才清楚数据类型。
关于javaType的指定,通过类型处理器的泛型,MyBatis 可以得知该类型处理器处理的 Java 类型,不过这种行为可以通过两种方法改变:
- 在类型处理器的配置元素(typeHandler 元素)上增加一个 javaType 属性(比如:javaType=“String”);
- 在类型处理器的类上增加一个 @MappedTypes 注解指定与其关联的 Java 类型列表。 如果在 javaType 属性中也同时指定,则注解上的配置将被忽略。
关于jdbcType的指定,可以通过两种方式来指定关联的 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 类型时的默认处理器。
2、配置处理器
全局配置
在mybatis配置文件中配置
<typeHandlers> <typeHandler handler="com.test.MyTypeHandler" /> </typeHandlers>
局部配置
只针对某个字段处理,查与增删改使用的typeHandler要保持一致
查:在resultMap中指定字段的typeHandler
<resultMap id = "result">
<result property="phone" column="phone" typeHandler="com.test.MyHandler"/>
</resultMap>
增删改:使用 #{} 指定typeHandler
<insert id="insertTestt"> insert into user_test(id, user_name, password) values (#{id}, #{userName}, #{password, typeHandler=com.test.MyTypeHandler}) </insert>