• 类型处理器(typeHandlers)

    类型处理器(typeHandlers)

    无论是 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
    ObjectTypeHandler Any OTHER 或未指定类型
    EnumTypeHandler Enumeration Type VARCHAR 或任何兼容的字符串类型,用以存储枚举的名称(而不是索引值)
    EnumOrdinalTypeHandler Enumeration 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 类型。比如:

    1. // ExampleTypeHandler.java
    2. @MappedJdbcTypes(JdbcType.VARCHAR)
    3. public class ExampleTypeHandler extends BaseTypeHandler<String> {
    4.  
    5. @Override
    6. public void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType) throws SQLException {
    7. ps.setString(i, parameter);
    8. }
    9.  
    10. @Override
    11. public String getNullableResult(ResultSet rs, String columnName) throws SQLException {
    12. return rs.getString(columnName);
    13. }
    14.  
    15. @Override
    16. public String getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
    17. return rs.getString(columnIndex);
    18. }
    19.  
    20. @Override
    21. public String getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
    22. return cs.getString(columnIndex);
    23. }
    24. }
    1. <!-- mybatis-config.xml -->
    2. <typeHandlers>
    3. <typeHandler handler="org.mybatis.example.ExampleTypeHandler"/>
    4. </typeHandlers>

    使用上述的类型处理器将会覆盖已经存在的处理 Java 的 String 类型属性和 VARCHAR 参数及结果的类型处理器。 要注意 MyBatis 不会通过窥探数据库元信息来决定使用哪种类型,所以你必须在参数和结果映射中指明那是 VARCHAR 类型的字段, 以使其能够绑定到正确的类型处理器上。这是因为 MyBatis 直到语句被执行时才清楚数据类型。

    通过类型处理器的泛型,MyBatis 可以得知该类型处理器处理的 Java 类型,不过这种行为可以通过两种方法改变:

    • 在类型处理器的配置元素(typeHandler 元素)上增加一个 javaType 属性(比如:javaType="String");
    • 在类型处理器的类上(TypeHandler class)增加一个 @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 类型,那么它将是 ResultMap 使用 Java 类型时的默认值(即使没有 includeNullJdbcType=true)。

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

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

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

    你可以创建一个能够处理多个类的泛型类型处理器。为了使用泛型类型处理器, 需要增加一个接受该类的 class 作为参数的构造器,这样在构造一个类型处理器的时候 MyBatis 就会传入一个具体的类。

    1. //GenericTypeHandler.java
    2. public class GenericTypeHandler<E extends MyObject> extends BaseTypeHandler<E> {
    3.  
    4. private Class<E> type;
    5.  
    6. public GenericTypeHandler(Class<E> type) {
    7. if (type == null) throw new IllegalArgumentException("Type argument cannot be null");
    8. this.type = type;
    9. }
    10. ...

    EnumTypeHandlerEnumOrdinalTypeHandler 都是泛型类型处理器,我们将会在接下来的部分详细探讨。