MyBatis - 配置(4) - typeHandlers

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/mytt_10566/article/details/82048748

官方文档:http://www.mybatis.org/mybatis-3/zh/configuration.html#typeHandlers

1. 示例

假设我们有这样一个需求:

  • 有个POJO:Address,包含省市区信息;数据库与之对应的列是:address varchar(100)
  • Address作为参数保存至数据库时,格式为:省 市 区
  • 数据库address映射到Address时,自动设置省市区信息

当然我们可以搞个PO类,将Address的省市区信息拼接后作为参数保存至数据库、数据库直接映射到PO中,再手动切割保存到Address中。这里只是为了使用Mybatis时,多提供一种解决思路,学习MyBatis中使用自定义的TypeHandler来处理上述需求。

(1)User、Address

public class User {
    private Integer id;
    private String userName;
    private Address address;
    
    // getter、setter略
}
public class Address {
	
    public Address() {
    }

    public Address(String address) {
        if (Objects.nonNull(address) && address.length() > 0) {
            String[] addrArray = address.split(" ");
            this.province = addrArray[0];
            this.city = addrArray[1];
            this.area = addrArray[2];
        }
    }

    private String province;
    private String city;
    private String area;
    
    // getter、setter略
}

(2)自定义类型处理器

可以通过实现 org.apache.ibatis.type.TypeHandler 接口, 或继承一个很便利的类 org.apache.ibatis.type.BaseTypeHandler, 然后可以选择性地将它映射到一个 JDBC 类型。

import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.MappedJdbcTypes;
import org.apache.ibatis.type.MappedTypes;

/**
 * Address <-> VARCHAR
 * */
@MappedJdbcTypes(value = JdbcType.VARCHAR, includeNullJdbcType = true)
@MappedTypes(Address.class)
public class AddressTypeHandler extends BaseTypeHandler<Address> {

    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, Address parameter, JdbcType jdbcType)
        throws SQLException {
        // 格式:省 市 区
        ps.setString(i, parameter.getProvince() + " " + parameter.getCity() + " " + parameter.getArea());
    }

    @Override
    public Address getNullableResult(ResultSet rs, String columnName) throws SQLException {
        return new Address(rs.getString(columnName));
    }

    @Override
    public Address getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
        return new Address(rs.getString(columnIndex));
    }

    @Override
    public Address getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
        return new Address(cs.getString(columnIndex));
    }	
}

mybatis-config.xml配置:

<!-- mybatis-config.xml -->
<typeHandlers>
    <package name="com.momo.mybatis.typehandler"/>
</typeHandlers>

<!-- 或 typeHandler标签 -->
<typeHandlers>
    <typeHandler handler="com.momo.mybatis.typehandler.AddressTypeHandler"/>
</typeHandlers>

(3)UserMapper.xml

<resultMap>标签中使用,用于查询时映射处理。此时java类型是已知的(从结果类型中获得),但是JDBC类型是未知的。各种情况如下:

  • 指定typeHandler属性:使用指定的处理器处理参数以及映射结果
  • 未指定typeHandler、jdbcType属性:使用javaType=[TheJavaType], jdbcType=null寻找合适的处理器
  • 未指定typeHandler、指定jdbcType属性,使用javaType=[TheJavaType], jdbcType=[TheJdbcType]寻找合适的处理器

一个或多个类型处理器:

  • 假如找到多个处理器,那么优先选用@MappedJdbcTypes注解有includeNullJdbcType=true的处理器,该属性值默认false。如果仍有多个满足条件,选用最后一个注册的处理器。
  • 若只找到一个处理器,那么即使没有includeNullJdbcType=true,也会使用该处理器
<resultMap type="User" id="userMap">
    <id property="id" column="id"/>
    <result property="userName" column="user_name"/>
    <result property="address" column="user_address" jdbcType="VARCHAR"/>
</resultMap>

若是作为参数使用,如新增、更新等,处理同上

<insert id="insert" parameterType="User">
    insert into t_user (user_name, user_address) 
    values(#{userName}, #{address,jdbcType=VARCHAR})
</insert>

注:

(1)通过类型处理器的泛型,MyBatis 可以知道该类型处理器处理的 Java 类型,有两种方法改变:

  • mybatis配置文件中,类型处理器的配置元素<typeHandler  javaType="xxx">上通过 javaType 属性指定
  • 在类型处理器的类上增加一个 @MappedTypes 注解来指定与其关联的 Java 类型列表。 如果在 javaType 属性中也同时指定,则注解方式将被忽略

(2)两种方式来指定被关联的 JDBC 类型:

  • mybatis配置文件中,类型处理器的配置元素<typeHandler  jdbcType="xxx">通过jdbcType 属性指定
  • 在类型处理器的类上增加一个 @MappedJdbcTypes 注解来指定与其关联的 JDBC 类型列表。 如果在 jdbcType 属性中也同时指定,则注解方式将被忽略。

(3)在使用<package name="com.momo.handler"/>注册类型处理器时,只能通过注解方式来指定 JDBC 的类型。

2. MyBatis内置的类型处理器

官方文档copy过来的,当做笔记用。。

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

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

类型处理器 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
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 类型,存储枚举的索引(而不是名称)。
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 or LONGVARCHAR
JapaneseDateTypeHandler java.time.chrono.JapaneseDate DATE

你可以重写类型处理器或创建你自己的类型处理器来处理不支持的或非标准的类型。 具体做法为:实现 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 element)上增加一个 javaType 属性(比如:javaType="String");
  • 在类型处理器的类上(TypeHandler class)增加一个 @MappedTypes 注解来指定与其关联的 Java 类型列表。 如果在 javaType 属性中也同时指定,则注解方式将被忽略。

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

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

当决定在ResultMap中使用某一TypeHandler时,此时java类型是已知的(从结果类型中获得),但是JDBC类型是未知的。 因此Mybatis使用javaType=[TheJavaType], jdbcType=null的组合来选择一个TypeHandler。 这意味着使用@MappedJdbcTypes注解可以限制TypeHandler的范围,同时除非显式的设置,否则TypeHandler在ResultMap中将是无效的。 如果希望在ResultMap中使用TypeHandler,那么设置@MappedJdbcTypes注解的includeNullJdbcType=true即可。 然而从Mybatis 3.4.0开始,如果只有一个注册的TypeHandler来处理Java类型,那么它将是ResultMap使用Java类型时的默认值(即使没有includeNullJdbcType=true)。

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

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

注意在使用自动检索(autodiscovery)功能的时候,只能通过注解方式来指定 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;
  }
  ...

猜你喜欢

转载自blog.csdn.net/mytt_10566/article/details/82048748