Skip to content

7.2.typehandler

abel533 edited this page Feb 26, 2018 · 3 revisions

7.2 TypeHandler 类型处理器

本文通过两个例子来演示 typeHandler 的用法。

示例来自 base/src/test/java/tk.mybatis.mapper.typehandler 包中的测试。

在开始例子前,先上相同的代码。

一个简单的枚举类:

public enum StateEnum {
    disabled,
    enabled,
}

这里打算用 MyBatis 提供的 EnumOrdinalTypeHandler,也是数据库存储枚举对应的序号。

一个简单的地址类:

public class Address implements Serializable {
    private static final long serialVersionUID = 1L;
    private String province;
    private String city;

    public String getProvince() {
        return province;
    }

    public void setProvince(String province) {
        this.province = province;
    }

    public String getCity() {
        return city;
    }

    public void setCity(String city) {
        this.city = city;
    }

    @Override
    public String toString() {
        StringBuilder builder = new StringBuilder();
        if(province != null && province.length() > 0){
            builder.append(province);
        }
        if(city != null && city.length() > 0){
            builder.append("/").append(city);
        }
        return builder.toString();
    }
}

这个类用来演示自定义类型的用法,针对该类写一个自定义的 TypeHandler,如下:

public class AddressTypeHandler extends BaseTypeHandler<Address> {
    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, Address parameter,
                                    JdbcType jdbcType) throws SQLException {
        ps.setString(i, parameter.toString());
    }

    private Address convertToAddress(String addressStr){
        if(addressStr == null || addressStr.length() == 0){
            return null;
        }
        String[] strings = addressStr.split("/");
        Address address = new Address();
        if(strings.length > 0 && strings[0].length() > 0){
            address.setProvince(strings[0]);
        }
        if(strings.length > 1 && strings[1].length() > 0){
            address.setCity(strings[1]);
        }
        return address;
    }

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

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

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

测试数据如下:

create table user (
  id integer NOT NULL PRIMARY KEY,
  name varchar(32),
  address varchar(64),
  state integer
);

INSERT INTO user (id, name, address, state) VALUES (1, 'abel533', 'Hebei/Shijiazhuang', 1);
INSERT INTO user (id, name, address, state) VALUES (2, 'isea533', 'Hebei/Handan', 0);

特别注意,state 对应的 0 就是枚举第一个 disabled,1 就是枚举第二个 enabled。

7.2.1 使用 ColumnType 注解指定

创建 user 表对应的 User 实体:

public class User implements Serializable {
    private static final long serialVersionUID = 1L;
    @Id
    private Integer   id;
    private String    name;
    @ColumnType(typeHandler = AddressTypeHandler.class)
    private Address   address;
    private StateEnum state;

    //省略 setter 和 getter
}

特别提醒

  1. 默认情况下只有简单类型才会被当作表中的字段(useSimpleType=true)。
  2. 当字段有 @Column 或者 @ColumnType 注解时,也会被当作表中的字段。
  3. 默认情况下,枚举不会当作表中的字段,如果想要自动把枚举作为表中字段,需要配置 enumAsSimpleType=true这里的例子就启用了这个配置。如果不启用这个功能,也需要加 @Column 或者 @ColumnType 注解。

在这个例子中,address 字段通过 @ColumnType 设置了 typeHandler。但是 state 却没这么设置,先看 EnumOrdinalTypeHandler 的定义:

public class EnumOrdinalTypeHandler<E extends Enum<E>> extends BaseTypeHandler<E>

由于需要指定泛型,因此这里不能直接配置,除非你在创建一个针对性的 TypeHandler,例如:

public class StateEnumTypeHandler extends EnumOrdinalTypeHandler<StateEnum> {
    public StateEnumTypeHandler(Class<StateEnum> type) {
        super(type);
    }
}

然后配置:

@ColumnType(typeHandler = StateEnumTypeHandler.class)
private StateEnum state;

除了用这种麻烦的方式外,还可以直接用下面的方式配置全局的 typeHandler:

<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">

<configuration>
    <!-- 其他配置 -->
    <typeHandlers>
        <typeHandler 
                handler="org.apache.ibatis.type.EnumOrdinalTypeHandler" 
                javaType="tk.mybatis.mapper.typehandler.StateEnum"/>
    </typeHandlers>
    <!-- 其他配置 -->
</configuration>

有了这些配置后,就可以在增删改查使用了。

这个例子对应的测试: tk.mybatis.mapper.typehandler.TypeHandlerTest

7.2.2 全局配置

第二种就是全部在配置文件中使用 typeHandler 进行配置,实体只做简单的配置:

@Table(name = "user")
public class User2 implements Serializable {
    private static final long serialVersionUID = 1L;
    @Id
    private Integer   id;
    private String    name;
    @Column
    private Address   address;
    private StateEnum state;

    //省略 setter 和 getter
}

这里的 Address 加上 @Column 注解,只是为了把该字段当成表中的字段。

typeHandler 全局配置如下:

<typeHandlers>
    <typeHandler 
                 handler="org.apache.ibatis.type.EnumOrdinalTypeHandler" 
                 javaType="tk.mybatis.mapper.typehandler.StateEnum"/>
    
    <typeHandler handler="tk.mybatis.mapper.typehandler.AddressTypeHandler"/>
</typeHandlers>

做好这些配置后,就可以在增删改查使用了。

这个例子对应的测试: tk.mybatis.mapper.typehandler.TypeHandlerTest2