如何在springboot優雅的使用枚舉
從數據庫中讀取枚舉值
使用Mybatis-Plus讀取
借助MyBatis-Plus可以很容易的實現這一點。
首先需要在配置文件中加入type-enums-package指定枚舉的掃描包,MyBatis-Plus將為包內(包含子包)所有枚舉進行適配,可以使用逗號或封號分隔多個包名。
mybatis-plus: type-enums-package: [枚舉包][,|;][枚舉包]
接着在枚舉類中指定數據庫值所對應的屬性。這里可以采用兩種方式。
-
實現官方提供的IEnum接口,接口中的getValue方法與數據庫值對應的屬性。
@Getter//實現getValue public enum StatusEnum implements IEnum<Integer> { VALID(1, "有效"), INVALID(0, "無效"); StatusEnum(Integer value, String desc) { this.value = value; this.desc = desc; } //標記數據庫存的值是value private final Integer value; private final String desc; }
- 將屬性使用EnumValue注解標記數據庫值對應的屬性。
@Getter//實現getValue public enum StatusEnum { VALID(1, "有效"), INVALID(0, "無效"); StatusEnum(Integer value, String desc) { this.value = value; this.desc = desc; } //標記數據庫存的值是value @EnumValue private final Integer value; private final String desc; }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
在類的屬性聲明上直接將字段類型標記為枚舉類型,讀取時將自動轉換數據庫值為枚舉對象。
@Data @Accessors(chain = true) @TableName("test") public class TestDO { private Integer id; private String username; private StatusEnum status; }
讀取數據庫中的兩條數據進行測試,可以看到值被成功轉換為了枚舉。
MyBatis-Plus的實現
從MyBatis-Plus MybatisSqlSessionFactoryBean中可以找到它是如何實現的。
在buildSqlSessionFactory方法中可以看到,在配置了type-enums-package的情況下,
MyBatis-Plus將為該包下滿足處理條件的枚舉注冊MybatisEnumTypeHandler類型轉換處理器
在MybatisEnumTypeHandler中將取出實現IEnum接口的枚舉的getValue方法或使用EnumValue標記的字段的getter方法進行數據庫值處理。
使用Mybatis實現
Mybatis提供了default-enum-type-handler配置用於改寫默認的枚舉處理器,這里簡單粗暴的直接替換了默認處理器(MyBatis-Plus是滿足條件的類才注冊為該處理器處理,實際情況也應該如此)。
mybatis: configuration: default-enum-type-handler: com.baomidou.mybatisplus.extension.handlers.MybatisEnumTypeHandler
同樣,這樣也能完成枚舉映射。
或者在原有的Mybatis配置下追加類似的處理器注冊操作。
@Configuration public class MybatisConfig implements InitializingBean { private final SqlSessionFactory sqlSessionFactory; public MybatisConfig(SqlSessionFactory sqlSessionFactory) { this.sqlSessionFactory = sqlSessionFactory; } @Override public void afterPropertiesSet() throws Exception { sqlSessionFactory.getConfiguration().getTypeHandlerRegistry().register(StatusEnum.class, MybatisEnumTypeHandler.class); } }
將請求值轉換為枚舉對象
雖然成功的將數據庫值讀為枚舉屬性,但如果不做處理,實體上的枚舉類型將會成為累贅使請求值無法轉換,因此需要處理使請求的字符串或數字值能夠轉換為枚舉對象。
普通請求
對於諸如Get請求,Post表單請求的普通請求,可以使用自定義的轉換器工廠進行處理,我們需要繼承ConverterFactory類並指定想要處理的類型。
@Slf4j public class StringToEnumConverterFactory implements ConverterFactory<String, Enum<?>> { private static final Map<Class<?>, Converter<String, ? extends Enum<?>>> CONVERTER_MAP = new ConcurrentHashMap<>(); private static final Map<Class<?>, Method> TABLE_METHOD_OF_ENUM_TYPES = new ConcurrentHashMap<>(); @Override @SuppressWarnings("unchecked cast") public <T extends Enum<?>> Converter<String, T> getConverter(Class<T> targetType) { // 緩存轉換器 Converter<String, T> converter = (Converter<String, T>) CONVERTER_MAP.get(targetType); if (converter == null) { converter = new StringToEnumConverter<>(targetType); CONVERTER_MAP.put(targetType, converter); } return converter; } static class StringToEnumConverter<T extends Enum<?>> implements Converter<String, T> { private final Map<String, T> enumMap = new ConcurrentHashMap<>(); StringToEnumConverter(Class<T> enumType) { Method method = getMethod(enumType); T[] enums = enumType.getEnumConstants(); // 將值與枚舉對象對應並緩存 for (T e : enums) { try { enumMap.put(method.invoke(e).toString(), e); } catch (IllegalAccessException | InvocationTargetException ex) { log.error("獲取枚舉值錯誤!!! ", ex); } } } @Override public T convert(@NotNull String source) { // 獲取 T t = enumMap.get(source); if (t == null) { throw new IllegalArgumentException("該字符串找不到對應的枚舉對象 字符串:" + source); } return t; } } public static <T> Method getMethod(Class