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