MyBatis踩坑记(一)之自定义TypeHandler

TypeHandler网上的许多文章都是关注于自定义,而并没有关注其执行过程。

TypeHandler是用于将数据库字段与Java类进行转换的一个类,往往我们需要自定义实现其的场景是Java中使用枚举类
而数据库中用INTEGER或者CHAR去代表

这个时候我们就需要自定义一个TypeHandler,这时候我们一般都是这样设计

  • POJO对应属性类型为枚举类型
  • 在SQL Statement时需要将属性转为数据库字段设计的类型 ** (Java => 数据库) **
  • 在结果集的时候将数据库字段设计的类型转换为Java类型 ** (数据库 => Java) **

自定义设计TypeHandler

这时候我们可以利用泛型写出一个通用的TypeHandler

public final class UniversalEnumHandler <E extends BaseEnum> implements TypeHandler<E> {
    private Class<E> type;
    private E [] enums;

    /**
     * 设置配置文件设置的转换类以及枚举类内容,供其他方法更便捷高效的实现
     * @param type 配置文件中设置的转换类
     */
    public UniversalEnumHandler(Class<E> type) {
        if (type == null)
            throw new IllegalArgumentException("Type argument cannot be null");
        System.out.println(type.toString());
        this.type = type;
        this.enums = type.getEnumConstants();
        if (this.enums == null)
            throw new IllegalArgumentException(type.getSimpleName()
                    + " does not represent an enum type.");
    }

    @Override
    public void setParameter(PreparedStatement preparedStatement, int i, E e, JdbcType jdbcType) throws SQLException {
        //用getInt因为我数据库里面是INTEGER,这个自己判断,下面的也是
        preparedStatement.setInt(i,(int)e.getValue());
    }

    @Override
    public E getResult(ResultSet resultSet, String s) throws SQLException {
        return locateEnumStatus(resultSet.getInt(s));
    }

    @Override
    public E getResult(ResultSet resultSet, int i) throws SQLException {
        return locateEnumStatus(resultSet.getInt(i));
    }

    @Override
    public E getResult(CallableStatement callableStatement, int i) throws SQLException {
        return locateEnumStatus(callableStatement.getInt(i));
    }

    private E locateEnumStatus(Integer value) {
        for(E e : enums) {
            if(e.getValue().equals(value)) {
                return e;
            }
        }
        throw new IllegalArgumentException("未知的枚举类型:" + value + ",请核对" + type.getSimpleName());
    }
}

使用TypeHandler

  • #{parm}中添加一个typeHandler属性就可以在SQL语句中调用自定义的TypeHandler.
  • @Result中添加对应属性的typeHandler

问题出现

而我的POJO只有一个构造器,是全参数的,这个时候问题就来了,这样子是行不通的,因为在接收到数据库返回的数据时,Mybatis是先生成
POJO对象,再调用TypeHandler的getResult,这个时候我们POJO的属性类型是枚举类,要用INTERGER或者CHAR填充调用构造器肯定就报错了
,因为对应属性构造器接收的是枚举类型。

因此正确的方法是再重载一个构造器,将对应需要Maping的属性写为数据库中的字段类型,在构造器中放弃这两个属性的Setter,因为其后TypeHandler
会帮助你把他Set好。这样问题就解决了。

KAI Java, MyBatis