typeHandler

发布时间 2023-04-08 17:04:58作者: zhengbiyu

一、什么是类型处理器

1、类型处理器(TypeHandler)

MyBatis 在设置预处理语句(PreparedStatement)中的参数或从结果集中取出一个值时,都会用类型处理器将获取到的值以合适的方式转换成 Java 类型。也就是java类型与jdbc类型之间的处换器,诸如最常见StringTypeHandler。

2、Mybatis内置了哪些类型处理器

如下图,类型处理器的顶级接口是org.apache.ibatis.type.TypeHandler。

下表描述了一些默认的类型处理器,官方原表。

类型处理器Java 类型JDBC 类型
BooleanTypeHandler java.lang.Booleanboolean 数据库兼容的 BOOLEAN
ByteTypeHandler java.lang.Bytebyte 数据库兼容的 NUMERIC 或 BYTE
ShortTypeHandler java.lang.Shortshort 数据库兼容的 NUMERIC 或 SMALLINT
IntegerTypeHandler java.lang.Integerint 数据库兼容的 NUMERIC 或 INTEGER
LongTypeHandler java.lang.Longlong 数据库兼容的 NUMERIC 或 BIGINT
FloatTypeHandler java.lang.Floatfloat 数据库兼容的 NUMERIC 或 FLOAT
DoubleTypeHandler java.lang.Doubledouble 数据库兼容的 NUMERIC 或 DOUBLE
BigDecimalTypeHandler java.math.BigDecimal 数据库兼容的 NUMERIC 或 DECIMAL
StringTypeHandler java.lang.String CHARVARCHAR
ClobReaderTypeHandler java.io.Reader -
ClobTypeHandler java.lang.String CLOBLONGVARCHAR
NStringTypeHandler java.lang.String NVARCHARNCHAR
NClobTypeHandler java.lang.String NCLOB
BlobInputStreamTypeHandler java.io.InputStream -
ByteArrayTypeHandler byte[] 数据库兼容的字节流类型
BlobTypeHandler byte[] BLOBLONGVARBINARY
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>

使用场景

用户敏感信息加密。