如何在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