首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

MyBatis 自定義 TypeHandler 處理枚舉:通用 DisplayedEnum 介面與 DefaultEnumTypeHandler 實戰

     編輯:Java 相關知識

內建 TypeHandler 限制

  • 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 方法透過反射自動獲取
}

通用 TypeHandler 實作

@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;
    }
}

MyBatis 映射配置

<resultMap id="xxx" type="xxx">
    <result column="type" jdbcType="TINYINT" property="type"
        typeHandler="com.xxx.DefaultEnumTypeHandler" />
</resultMap>

關鍵:必須在 <result> 或 <parameter> 上指定 typeHandler,否則 MyBatis 預設用 EnumTypeHandler。

優勢總結

  • **一次實作,處處可用**:所有實作 DisplayedEnum 的枚舉自動支援
  • **資料庫存 value,Java 用物件,JSON 序列化輸出 label**
  • **反射獲取欄位**,枚舉類無需寫死 getter
  • **靜態工廠方法** 方便其他場景手動轉換

擴展提示

枚舉類型增多時,每個 ResultMap 仍需手動指定 typeHandler。下一步可實作 TypeHandlerRegistry 自動掃描註冊,實現零配置。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved