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好。这样问题就解决了。