EnumTypeHandler:儲存枚舉名稱(如 "NORMAL")EnumOrdinalTypeHandler:儲存枚舉序數(如 0)實際專案常需:**資料庫儲存業務值 (value),前端顯示標籤 (label)**,內建無法滿足。
public interface DisplayedEnum {
String DEFAULT_VALUE_NAME = "value";
String DEFAULT_LABEL_NAME = "label";
// 透過反射獲取 value 欄位
default Integer getValue() {
Field field = ReflectionUtils.findField(this.getClass(), DEFAULT_VALUE_NAME);
if (field == null) return null;
field.setAccessible(true);
return Integer.parseInt(field.get(this).toString());
}
// 透過反射獲取 label 欄位,配合 @JsonValue 直接序列化
@JsonValue
default String getLabel() {
Field field = ReflectionUtils.findField(this.getClass(), DEFAULT_LABEL_NAME);
if (field == null) return null;
field.setAccessible(true);
return field.get(this).toString();
}
// 靜態工廠:根據 value 反查枚舉實例
static <T extends Enum<T>> T valueOfEnum(Class<T> enumClass, Integer value) {
if (value == null) throw new IllegalArgumentException("value 不能為空");
T[] enums = enumClass.getEnumConstants();
for (T t : enums) {
if (((DisplayedEnum) t).getValue().equals(value)) return t;
}
throw new IllegalArgumentException("無法解析: " + value + " → " + enumClass.getName());
}
}
public enum CommonsType implements DisplayedEnum {
NORMAL("正常", 0),
INVALID("無效", 1);
private final String label;
private final Integer value;
private CommonsType(String label, Integer value) {
this.label = label;
this.value = value;
}
// 無需額外 getter,介面 default 方法透過反射自動獲取
}
@MappedJdbcTypes(value = JdbcType.TINYINT, includeNullJdbcType = true)
public class DefaultEnumTypeHandler extends BaseTypeHandler<DisplayedEnum> {
private final Class<DisplayedEnum> type;
public DefaultEnumTypeHandler() {}
public DefaultEnumTypeHandler(Class<DisplayedEnum> type) {
if (type == null) throw new IllegalArgumentException("Type argument cannot be null");
this.type = type;
}
@Override
public void setNonNullParameter(PreparedStatement ps, int i, DisplayedEnum param, JdbcType jdbcType) throws SQLException {
ps.setInt(i, param.getValue()); // 寫入 value
}
@Override
public DisplayedEnum getNullableResult(ResultSet rs, String columnName) throws SQLException {
return convert(rs.getInt(columnName));
}
@Override
public DisplayedEnum getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
return convert(rs.getInt(columnIndex));
}
@Override
public DisplayedEnum getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
return convert(cs.getInt(columnIndex));
}
private DisplayedEnum convert(int value) {
DisplayedEnum[] enums = type.getEnumConstants();
for (DisplayedEnum e : enums) {
if (e.getValue() == value) return e;
}
return null;
}
}
<resultMap id="xxx" type="xxx">
<result column="type" jdbcType="TINYINT" property="type"
typeHandler="com.xxx.DefaultEnumTypeHandler" />
</resultMap>
關鍵:必須在 <result> 或 <parameter> 上指定 typeHandler,否則 MyBatis 預設用 EnumTypeHandler。
DisplayedEnum 的枚舉自動支援枚舉類型增多時,每個 ResultMap 仍需手動指定 typeHandler。下一步可實作 TypeHandlerRegistry 自動掃描註冊,實現零配置。