泛型 TypeHandler

在 Mybatis 自带的 TypeHandler 中就有通过泛型 TypeHandler 支持的类型,比如枚举的 TypeHandler 就分别有 EnumOrdinalTypeHandlerEnumTypeHandler 两种。分别使用枚举的 ordinal 以及 name 来存取枚举。这两种 TypeHandler 都通过继承 BaseTypeHandler<E> 实现的。

考虑如下的一个场景:我希望对一系列某个接口的子类实例进行处理,如果针对每一个子类实现一个 TypeHandler 会非常麻烦,而我们在序列化和反序列化子类实例的时候都使用接口方法的返回值,那么我们就可以定义一个泛型 TypeHandler。下面给出一个枚举结合泛型 TypeHandler 的例子。

首先定义一个接口:

  
  package com.avaloninc.generictypehandler.domain;
  
  public interface Translatable {
    int getCode();
  
    String getNameCn();
  }

然后定义一个实现该接口的枚举:

  
  package com.avaloninc.generictypehandler.domain;
  
  public enum Gender implements Translatable {
  
    MALE(1, "男"),
    FEMALE(2, "女");
  
    private String nameCn;
    private int code;
  
    Gender(int code, String nameCn) {
      this.code = code;
      this.nameCn = nameCn;
    }
  
    @Override
    public int getCode() {
      return this.code;
    }
  
    @Override
    public String getNameCn() {
      return this.nameCn;
    }
  }

然后我们准备用自定义的 code 来进行存取,而不是枚举的 ordinal (注意 ordinal 和枚举常量的定义顺序是一致的,默认从 0 开始)。

下面给出实体类:

  
  package com.avaloninc.generictypehandler.domain;
  
  import lombok.Data;
  
  @Data
  public class Person {
      private int    id;
      private String name;
      private int    age;
      private Gender gender;
  }

定义泛型 TypeHandler,注意我们这里使用了指定了泛型的上界:枚举并且实现了接口。

  
  package com.avaloninc.generictypehandler.typehandler;
  
  
  import com.avaloninc.generictypehandler.domain.Translatable;
  import org.apache.ibatis.type.BaseTypeHandler;
  import org.apache.ibatis.type.JdbcType;
  
  import java.sql.CallableStatement;
  import java.sql.PreparedStatement;
  import java.sql.ResultSet;
  import java.sql.SQLException;
  import java.util.Arrays;
  import java.util.Objects;
  import java.util.Optional;
  
  public class GenericTranslatableEnumHandler<T extends Enum<T> & Translatable> extends BaseTypeHandler<T> {
  
    private Class<T> type;
  
    public GenericTranslatableEnumHandler(Class<T> type) {
      if (Objects.isNull(type)) {
        throw new IllegalArgumentException("type should not be null.");
      }
      this.type = type;
    }
  
    @Override
    public void setNonNullParameter(PreparedStatement preparedStatement, int i, T t, JdbcType
        jdbcType) throws SQLException {
      preparedStatement.setInt(i, t.getCode());
    }
  
    @Override
    public T getNullableResult(ResultSet resultSet, String s) throws SQLException {
      int code = resultSet.getInt(s);
      Optional<T> first = Arrays.stream(type.getEnumConstants())
          .filter(ele -> ele.getCode() == code).findFirst();
      if (first.isPresent()) {
        return first.get();
      } else {
        throw new SQLException("Illegal argument " + code + " for " + type.getCanonicalName());
      }
    }
  
    @Override
    public T getNullableResult(ResultSet resultSet, int i) throws SQLException {
      int code = resultSet.getInt(i);
      Optional<T> first = Arrays.stream(type.getEnumConstants())
          .filter(ele -> ele.getCode() == code).findFirst();
      if (first.isPresent()) {
        return first.get();
      } else {
        throw new SQLException("Illegal argument " + code + " for " + type.getCanonicalName());
      }
    }
  
    @Override
    public T getNullableResult(CallableStatement callableStatement, int i) throws SQLException {
      int code = callableStatement.getInt(i);
      Optional<T> first = Arrays.stream(type.getEnumConstants())
          .filter(ele -> ele.getCode() == code).findFirst();
      if (first.isPresent()) {
        return first.get();
      } else {
        throw new SQLException("Illegal argument " + code + " for " + type.getCanonicalName());
      }
    }
  }
  

mybatis-config.xml 中指定类型和 TypeHandler 的关联:

  
      <typeHandlers>
          <typeHandler handler="com.avaloninc.generictypehandler.typehandler.GenericTranslatableEnumHandler"
                       javaType="com.avaloninc.generictypehandler.domain.Gender"/>
      </typeHandlers>

最后是 Mapper 接口:

  
  package com.avaloninc.generictypehandler.mapper;
  
  import com.avaloninc.generictypehandler.domain.Person;
  import org.apache.ibatis.annotations.Insert;
  import org.apache.ibatis.annotations.Mapper;
  import org.apache.ibatis.annotations.Options;
  import org.apache.ibatis.annotations.Param;
  import org.apache.ibatis.annotations.Select;
  
  @Mapper
  public interface PersonMapper {
      /**
       * Insert int.
       *
       * @param person the person
       * @return the int
       */
      @Insert("insert into person (name, age, gender) values (#{p.name}, #{p.age}, #{p.gender})")
      @Options(useGeneratedKeys = true, keyProperty = "p.id")
      int insert(@Param("p") Person person);
  
      /**
       * Get person.
       *
       * @param id the id
       * @return the person
       */
      @Select("select id, name, age, gender from person where id = #{id}")
      Person get(@Param("id") int id);
  }

发表评论

电子邮件地址不会被公开。 必填项已用*标注