mybatis TypeHandler處理自定義列舉型別
之前做一個專案一個表字段中有很多狀態,通過狀態碼來實現各個狀態,但是在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<E> type;
private E[] enums;
public MyEnumTypeHandler(Class<E> 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<T> {
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註解里加新列舉即可。