之前做一个项目一个表字段中有很多状态,通过状态码来实现各个状态,但是在java 实体类中用int类型表示状态码,就出现一堆魔鬼数字。如果这样,那么后期代码维护和可读性比较困难,如果没有完整注释。
后来在官方参考 中发现mybatis提供两种枚举类型转换器:EnumTypeHandler(默认),EnumOrdinalTypeHandler.
EnumTypeHandler主要将字段值转换成对应枚举对象。通过Enum.valueOf(class,string)
方法实现。部分源码如下
@Override
public E getNullableResult(ResultSet rs, String columnName) throws SQLException {
String s = rs.getString(columnName);
return s == null ? null : Enum.valueOf(type, s);
}
EnumOrdinalTypeHandler 是将字段值(必须数字类型)作为枚举下标搜索对应枚举对象.部分源码如下
@Override
public E getNullableResult(ResultSet rs, String columnName) throws SQLException {
int i = rs.getInt(columnName);
if (rs.wasNull()) {
return null;
} else {
try {
return enums[i];
} catch (Exception ex) {
throw new IllegalArgumentException("Cannot convert " + i + " to " + type.getSimpleName() + " by ordinal value.", ex);
}
}
}
int i = rs.getInt(columnName);
i是返回字段值,然后通过return enums[i];
返回对应下标枚举对象。
但是很多时候码是根据业务确定的不一定是按照枚举下标来表示的。没办法这个时候只能自己动手,但是可以参考EnumOrdinalTypeHandler 做法,替换return enums[i];
代码
自定义枚举类型转换器MyEnumTypeHandler完整代码如下:
public class MyEnumTypeHandler<E extends Enum<E>&BaseEnum<E>> extends BaseTypeHandler<E> {
private Class type;
private E[] enums;
public MyEnumTypeHandler(Class type) throws InstantiationException, IllegalAccessException{
if (type == null) {
throw new IllegalArgumentException("Type argument cannot be null");
}
this.type=type;
this.enums=this.type.getEnumConstants();
if(this.enums==null){
throw new IllegalArgumentException(type.getSimpleName() + " does not represent an enum type.");
}
}
@Override
public void setNonNullParameter(PreparedStatement ps, int i, E parameter, JdbcType jdbcType) throws SQLException {
ps.setInt(i, parameter.getCode());
}
@Override
public E getNullableResult(ResultSet rs, String columnName) throws SQLException {
int i = rs.getInt(columnName);
if (rs.wasNull()) {
return null;
} else {
try {
return getEnum(i);
} catch (Exception ex) {
throw new IllegalArgumentException("Cannot convert " + i + " to " + type.getSimpleName() + " by ordinal value.", ex);
}
}
}
@Override
public E getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
int i = rs.getInt(columnIndex);
if (rs.wasNull()) {
return null;
} else {
try {
return getEnum(i);
} catch (Exception ex) {
throw new IllegalArgumentException("Cannot convert " + i + " to " + type.getSimpleName() + " by ordinal value.", ex);
}
}
}
@Override
public E getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
int i = cs.getInt(columnIndex);
if (cs.wasNull()) {
return null;
} else {
try {
return getEnum(i);
} catch (Exception ex) {
throw new IllegalArgumentException("Cannot convert " + i + " to " + type.getSimpleName() + " by ordinal value.", ex);
}
}
}
private E getEnum(int code){
for(E tmpE:this.enums){
if(tmpE.getCode()==code){
return tmpE;
}
}
return null;
}
}
BaseEnum 代码:
public interface BaseEnum {
int getCode();
String getName();
T getEnumByCode(int code);
}
BaseEnum 接口是所有枚举必须继承,主要为了统一性。这样一个TypeHandler可以实现很多自定义enum.
在完成typeHandler之后需要将其加入到mybatis 配置文件中。
<typeHandlers>
<typeHandler handler="com.test.mybatis.typeHandler.MyEnumTypeHandler" javaType="XXEnum" />
typeHandlers>
考虑到如果很多枚举出现,不能都要修改一次配置文件,而且变化仅仅是javaType。后来发现mybatis提供一个MappedTypes 注解。只要在自定义typeHandler上实现即可。
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface MappedTypes {
Class>[] value();
}
这个注解value是数组类型,所以可以更方便添加。这样mybatis配置文件中只要将javaType属性去掉即可,后期再多枚举也不用改动mybatis配置文件,只要在MappedTypes注解里加新枚举即可。