2.5. 通用枚举字典接口
有时可以将 枚举 理解为系统的一类字段,比较典型的就是管理页面的各种下拉框,下拉框中的数据来自于后台服务。
有了 CommonEnum 之后,可以提供统一的一组枚举字典,避免重复开发,同时在新增枚举时也无需进行扩展,系统自动识别并添加到字典中。
2.5.1. 构建字典Controller
在 CommonEnumRegistry 基础之上实现通用字典接口非常简单,只需按规范构建 Controller 即可,具体如下:
@Api(tags = "通用字典接口") @RestController @RequestMapping("/enumDict") @Slf4j public class EnumDictController { @Autowired private CommonEnumRegistry commonEnumRegistry; @GetMapping("all") public RestResult<Map<String, List<CommonEnumVO>>> allEnums(){ Map<String, List<CommonEnum>> dict = this.commonEnumRegistry.getNameDict(); Map<String, List<CommonEnumVO>> dictVo = Maps.newHashMapWithExpectedSize(dict.size()); for (Map.Entry<String, List<CommonEnum>> entry : dict.entrySet()){ dictVo.put(entry.getKey(), CommonEnumVO.from(entry.getValue())); } return RestResult.success(dictVo); } @GetMapping("types") public RestResult<List<String>> enumTypes(){ Map<String, List<CommonEnum>> dict = this.commonEnumRegistry.getNameDict(); return RestResult.success(Lists.newArrayList(dict.keySet())); } @GetMapping("/{type}") public RestResult<List<CommonEnumVO>> dictByType(@PathVariable("type") String type){ Map<String, List<CommonEnum>> dict = this.commonEnumRegistry.getNameDict(); List<CommonEnum> commonEnums = dict.get(type); return RestResult.success(CommonEnumVO.from(commonEnums)); } }
该 Controller 提供如下能力:
- 获取全部字典,一次性获取系统中所有的 CommonEnum
- 获取所有字典类型,仅获取字典类型,通常用于测试
- 获取指定字典类型的全部信息,比如上述所说的填充下拉框
2.5.2. 效果展示
获取全部字典:
获取所有字典类型:
获取指定字段类型的全部信息:
2.6. 输出适配器
输出适配器主要以 ORM 框架为主,同时各类 ORM 框架均提供了类型映射的扩展点,通过该扩展点可以对 CommonEnum 使用 code 进行存储。
2.6.1. MyBatis 支持
MyBatis 作为最流行的 ORM 框架,提供了 TypeHandler 用于处理自定义的类型扩展。
@MappedTypes(NewsStatus.class) public class MyBatisNewsStatusHandler extends CommonEnumTypeHandler<NewsStatus> { public MyBatisNewsStatusHandler() { super(NewsStatus.values()); } }
MyBatisNewsStatusHandler 通过 @MappedTypes(NewsStatus.class) 对其进行标记,以告知框架该 Handler 是用于 NewsStatus 类型的转换。
CommonEnumTypeHandler 是为 CommonEnum 提供的通用转化能力,具体如下:
public abstract class CommonEnumTypeHandler<T extends Enum<T> & CommonEnum> extends BaseTypeHandler<T> { private final List<T> commonEnums; protected CommonEnumTypeHandler(T[] commonEnums){ this(Arrays.asList(commonEnums)); } protected CommonEnumTypeHandler(List<T> commonEnums) { this.commonEnums = commonEnums; } @Override public void setNonNullParameter(PreparedStatement preparedStatement, int i, T t, JdbcType jdbcType) throws SQLException { preparedStatement.setInt(i, t.getCode()); } @Override public T getNullableResult(ResultSet resultSet, String columnName) throws SQLException { int code = resultSet.getInt(columnName); return commonEnums.stream() .filter(commonEnum -> commonEnum.match(String.valueOf(code))) .findFirst() .orElse(null); } @Override public T getNullableResult(ResultSet resultSet, int i) throws SQLException { int code = resultSet.getInt(i); return commonEnums.stream() .filter(commonEnum -> commonEnum.match(String.valueOf(code))) .findFirst() .orElse(null); } @Override public T getNullableResult(CallableStatement callableStatement, int i) throws SQLException { int code = callableStatement.getInt(i); return commonEnums.stream() .filter(commonEnum -> commonEnum.match(String.valueOf(code))) .findFirst() .orElse(null); } }
由于逻辑比较简单,在此不做过多解释。
有了类型之后,需要在 spring boot 的配置文件中指定 type-handler 的加载逻辑,具体如下:
mybatis: type-handlers-package: com.geekhalo.lego.enums.mybatis
完成配置后,使用 Mapper 对数据进行持久化,数据表中存储的便是 code 信息,具体如下:
2.6.2. JPA 支持
随着 Spring data 越来越流行,JPA 又焕发出新的活力,JPA 提供 AttributeConverter 以对属性转换进行自定义。
首先,构建 JpaNewsStatusConverter,具体如下:
public class JpaNewsStatusConverter extends CommonEnumAttributeConverter<NewsStatus> { public JpaNewsStatusConverter() { super(NewsStatus.values()); } }
CommonEnumAttributeConverter 为 CommonEnum 提供的通用转化能力,具体如下:
public abstract class CommonEnumAttributeConverter<E extends Enum<E> & CommonEnum> implements AttributeConverter<E, Integer> { private final List<E> commonEnums; public CommonEnumAttributeConverter(E[] commonEnums){ this(Arrays.asList(commonEnums)); } public CommonEnumAttributeConverter(List<E> commonEnums) { this.commonEnums = commonEnums; } @Override public Integer convertToDatabaseColumn(E e) { return e.getCode(); } @Override public E convertToEntityAttribute(Integer code) { return (E) commonEnums.stream() .filter(commonEnum -> commonEnum.match(String.valueOf(code))) .findFirst() .orElse(null); } }
在有了 JpaNewsStatusConverter 之后,我们需要在 Entity 的属性上增加配置信息,具体如下:
@Entity @Data @Table(name = "t_jpa_news") public class JpaNewsEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Convert(converter = JpaNewsStatusConverter.class) private NewsStatus status; }
@Convert(converter = JpaNewsStatusConverter.class) 是对 status 的配置,使用 JpaNewsStatusConverter 进行属性的转换。
运行持久化指令后,数据库如下:
基于 Spring Cloud Alibaba + Gateway + Nacos + RocketMQ + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能
3. 项目信息
项目仓库地址:https://gitee.com/litao851025/lego