引言
typeHandlers
閱讀官方文檔 typeHandlers 一節 {:target="_blank"}
MyBatis 在預處理語句(PreparedStatement)中設置一個參數時,Java對象將通過ps.setInt、ps.setString、ps.setTimeStamp等方法轉換成數據庫需要的數據
在從結果集(ResultSet)中取出一個值時,將使用rs.getInt、rs.getString、rs.getTimeStamp等方法將數據轉換為Java對象
這兩類操作通過 類型處理器 完成
類型處理器均實現
TypeHandler<T>
接口,所有基本類型均有對應的類型處理器Enum對應有兩個類型處理器,分別為
EnumTypeHandler
、EnumOrdinalTypeHandler
若需將Enum字段映射為字符串,則使用
EnumTypeHandler
。 (默認使用)若需將Enum字段映射為int數值,則使用
EnumOrdinalTypeHandler
然而, EnumOrdinalTypeHandler
局限性非常明顯,其映射的數據直接使用枚舉值的 ordinal
數值,因此與枚舉值定義順序緊耦合, 本文將解決此問題
一般合理實現方案(針對每一種特定值Enum定義專屬TypeHandler)
假設存在一個產品類型枚舉類型定義,其產品均對應有特定int值,實現如下
public enum ProductType {
AAA(100),
BBB(200),
CCC(300);
private int value;
private ProductType(int value) {
this.value = value;
}
public static ProductType fromValue(int value) {
for (ProductType productType : ProductType.values()) {
if (productType.value == value) {
return productType;
}
}
throw new IllegalArgumentException("Cannot create evalue from value: " + value + "!");
}
public int toValue() {
return value;
}
}
同時,需要定義對應的 ProductTypeHandler
public class ProductTypeHandler implements TypeHandler<ProductType> {
@Overridepublic void setParameter(PreparedStatement preparedStatement, int i, ProductType productType, JdbcType jdbcType)throws SQLException {
preparedStatement.setInt(i, productType.toValue());
}
@Overridepublic ProductType getResult(ResultSet resultSet, String s) throws SQLException {
return ProductType.fromValue(resultSet.getInt(s));
}
@Overridepublic ProductType getResult(ResultSet resultSet, int i) throws SQLException {
return ProductType.fromValue(resultSet.getInt(i));
}
@Overridepublic ProductType getResult(CallableStatement callableStatement, int i) throws SQLException {
return ProductType.fromValue(callableStatement.getInt(i));
}
}
最佳實踐
同樣以上方的 ProductType
舉例,枚舉類型的定義稍作修改
public enum ProductType implements ValuedEnum {
AAA(100),
BBB(200),
CCC(300);
private int value;
private ProductType(int value) {
this.value = value;
}
public int getValue() {
return value;
}
}
可以看出,該類型實現了接口 ValuedEnum
,同時去除了 fromValue
類方法, ValuedEnum
接口內容如下,僅聲明一個 getValue
方法
public interface ValuedEnum {
int getValue();
}
不再需要為 ProductType
專屬定義類型轉換器,使用通用轉換器 ValuedEnumTypeHanlder
,使用方式同內置轉換器 EnumOrdinalTypeHandler
完全一致。 實現如下
/** * Created by sunlin05 on 2015/7/6. * @author sunlin */
public class ValuedEnumTypeHandler<E extends Enum<E>> extends BaseTypeHandler<E> {
private Class<E> type;
private Map<Integer, E> map = new HashMap<>();
public ValuedEnumTypeHandler(Class<E> type) {
if (type == null) {
throw new IllegalArgumentException("Type argument cannot be null");
}
this.type = type;
E[] enums = type.getEnumConstants();
if (enums == null) {
throw new IllegalArgumentException(type.getSimpleName() + " does not represent an enum type.");
}
for (E e : enums) {
ValuedEnum valuedEnum = (ValuedEnum) e;
map.put(valuedEnum.getValue(), e);
}
}
@Override
public void setNonNullParameter(PreparedStatement ps, int i, E parameter, JdbcType jdbcType) throws SQLException {
ValuedEnum valuedEnum = (ValuedEnum) parameter;
ps.setInt(i, valuedEnum.getValue());
}
@Override
public E getNullableResult(ResultSet rs, String columnName) throws SQLException {
int i = rs.getInt(columnName);
if (rs.wasNull()) {
return null;
} else {
return getValuedEnum(i);
}
}
@Override
public E getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
int i = rs.getInt(columnIndex);
if (rs.wasNull()) {
return null;
} else {
return getValuedEnum(i);
}
}
@Override
public E getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
int i = cs.getInt(columnIndex);
if (cs.wasNull()) {
return null;
} else {
return getValuedEnum(i);
}
}
private E getValuedEnum(int value) {
try {
return map.get(value);
} catch (Exception ex) {
throw new IllegalArgumentException(
"Cannot convert " + value + " to " + type.getSimpleName() + " by value.", ex);
}
}
}
該實現參考 EnumOrdinalTypeHandler
源碼,核心邏輯見構造器,加注釋說明如下
// 獲取所有枚舉值
E[] enums = type.getEnumConstants();
for (E e : enums) {
// 類型轉換為ValuedEnum接口對象
ValuedEnum valuedEnum = (ValuedEnum) e;
// 通過getValue()方法獲取枚舉值對應的Value int值,通過map記錄映射關系
map.put(valuedEnum.getValue(), e);
}