7.2 TypeHandler 类型处理器

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

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

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

一个简单的枚举类:

  1. public enum StateEnum {
  2. disabled,
  3. enabled,
  4. }

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

一个简单的地址类:

  1. public class Address implements Serializable {
  2. private static final long serialVersionUID = 1L;
  3. private String province;
  4. private String city;
  5. public String getProvince() {
  6. return province;
  7. }
  8. public void setProvince(String province) {
  9. this.province = province;
  10. }
  11. public String getCity() {
  12. return city;
  13. }
  14. public void setCity(String city) {
  15. this.city = city;
  16. }
  17. @Override
  18. public String toString() {
  19. StringBuilder builder = new StringBuilder();
  20. if(province != null && province.length() > 0){
  21. builder.append(province);
  22. }
  23. if(city != null && city.length() > 0){
  24. builder.append("/").append(city);
  25. }
  26. return builder.toString();
  27. }
  28. }

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

  1. public class AddressTypeHandler extends BaseTypeHandler<Address> {
  2. @Override
  3. public void setNonNullParameter(PreparedStatement ps, int i, Address parameter,
  4. JdbcType jdbcType) throws SQLException {
  5. ps.setString(i, parameter.toString());
  6. }
  7. private Address convertToAddress(String addressStr){
  8. if(addressStr == null || addressStr.length() == 0){
  9. return null;
  10. }
  11. String[] strings = addressStr.split("/");
  12. Address address = new Address();
  13. if(strings.length > 0 && strings[0].length() > 0){
  14. address.setProvince(strings[0]);
  15. }
  16. if(strings.length > 1 && strings[1].length() > 0){
  17. address.setCity(strings[1]);
  18. }
  19. return address;
  20. }
  21. @Override
  22. public Address getNullableResult(ResultSet rs, String columnName) throws SQLException {
  23. return convertToAddress(rs.getString(columnName));
  24. }
  25. @Override
  26. public Address getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
  27. return convertToAddress(rs.getString(columnIndex));
  28. }
  29. @Override
  30. public Address getNullableResult(CallableStatement cs, int columnIndex)
  31. throws SQLException {
  32. return convertToAddress(cs.getString(columnIndex));
  33. }
  34. }

测试数据如下:

  1. create table user (
  2. id integer NOT NULL PRIMARY KEY,
  3. name varchar(32),
  4. address varchar(64),
  5. state integer
  6. );
  7. INSERT INTO user (id, name, address, state) VALUES (1, 'abel533', 'Hebei/Shijiazhuang', 1);
  8. INSERT INTO user (id, name, address, state) VALUES (2, 'isea533', 'Hebei/Handan', 0);

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

7.2.1 使用 ColumnType 注解指定

创建 user 表对应的 User 实体:

  1. public class User implements Serializable {
  2. private static final long serialVersionUID = 1L;
  3. @Id
  4. private Integer id;
  5. private String name;
  6. @ColumnType(typeHandler = AddressTypeHandler.class)
  7. private Address address;
  8. private StateEnum state;
  9. //省略 setter 和 getter
  10. }

特别提醒

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

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

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

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

  1. public class StateEnumTypeHandler extends EnumOrdinalTypeHandler<StateEnum> {
  2. public StateEnumTypeHandler(Class<StateEnum> type) {
  3. super(type);
  4. }
  5. }

然后配置:

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

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

  1. <!DOCTYPE configuration
  2. PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
  3. "http://mybatis.org/dtd/mybatis-3-config.dtd">
  4. <configuration>
  5. <!-- 其他配置 -->
  6. <typeHandlers>
  7. <typeHandler
  8. handler="org.apache.ibatis.type.EnumOrdinalTypeHandler"
  9. javaType="tk.mybatis.mapper.typehandler.StateEnum"/>
  10. </typeHandlers>
  11. <!-- 其他配置 -->
  12. </configuration>

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

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

7.2.2 全局配置

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

  1. @Table(name = "user")
  2. public class User2 implements Serializable {
  3. private static final long serialVersionUID = 1L;
  4. @Id
  5. private Integer id;
  6. private String name;
  7. @Column
  8. private Address address;
  9. private StateEnum state;
  10. //省略 setter 和 getter
  11. }

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

typeHandler 全局配置如下:

  1. <typeHandlers>
  2. <typeHandler
  3. handler="org.apache.ibatis.type.EnumOrdinalTypeHandler"
  4. javaType="tk.mybatis.mapper.typehandler.StateEnum"/>
  5. <typeHandler handler="tk.mybatis.mapper.typehandler.AddressTypeHandler"/>
  6. </typeHandlers>

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

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