Java中使用枚举(enum)还是常量?枚举!

缺乏、安全感 2022-12-03 15:57 644阅读 0赞

这里先说结论:对于一组关联的数值,出于对数据安全的考虑,我们选择使用enum。

问题

定义表结构的时候经常会碰到一类字段:状态 ( status 或者 state ) 、类型 ( type ) ,而通常的做法一般是:

  • 数据库 中定义 tinyint 类型。
    比如:status tinyint(1) NOT NULL COMMENT ‘订单状态 1-待支付;2-待发货;3-待收货;4-已收货;5-已完结;’
  • Java 实体类 中定义 Short 类型。
    比如:private Short status

然后项目中可能会充斥着下面这样的代码:

  1. order.setStatus((short) 1);
  2. if (order.getStatus() == 1) {
  3. order.setStatus((short) 2);
  4. }
  5. if (order.getStatus() == 4) {
  6. order.setStatusName("已收货");
  7. }

这都是些什么魔鬼数字啊,没有注释根本没法看,如果手滑可能状态就设错了,而且不好排查是在哪处赋值的。

改进方案是用 常量 ,但是又会产生另一种效果:

  1. public static final Short WAIT_PAY = 1;
  2. if (WAIT_PAY.equals(order.getStatus())) {
  3. // 混用了解下
  4. order.setStatus((short) 2);
  5. }

定义常量对使用者没有约束力,仍然可能会被程序员无视,而直接使用数字。

这时候就该 枚举 出场了,枚举 的本质就是 类 + 常量 ,可以使用 枚举 来定义 一组 相关的元数据 ( 值、描述及其他必要信息 ) ,使用 枚举 类型不仅减小了数据维护 ( 比如调整了值的定义 ) 的成本,还加强了代码的 约束力 。

我们在实体类(DO/DTO/BO/VO/PO)和方法参数中,统一使用枚举类型的属性来保存有固定取值的字段。

要做到上面这一点,有下面三个地方要注意

  • 把接口的参数转化为枚举类型
  • 把接口返回的枚举类型转为String
  • 把枚举类型保存到数据库中

把接口的参数转化为枚举类型

Spring 默认使用Bean接收枚举参数时支持 字面量,这也是我们常见的做法。

如下:

  1. @Data
  2. public class UserCommand {
  3. private String name;
  4. private Gender gender;
  5. private String email;
  6. }
  7. @ApiOperation("添加用户")
  8. @PostMapping("/users")
  9. public User users(User command){
  10. User user = new User();
  11. BeanUtils.copyProperties(command,user);
  12. return user;
  13. }

在这里插入图片描述注意这种方式不支持枚举的ordinal值

把接口返回的枚举类型转为String

《阿里巴巴Java开发手册》将接口中枚举的使用分为两类,即 接口参数和接口返回值,并规定: 接口参数可以使用枚举类型,但接口返回值不可以使用枚举类型(包括含枚举类型的POJO对象)。
  这个主要是对dubbo等使用tcp协议的rpc接口调用来说的,为了避免序列化异常;对于spring cloud等使用http协议的框架,没有此限制。
  jackson 解析枚举 时,默认会返回字面量( MALE,FEMALE)。也可以通过@JsonValue指定要返回的属性:

  1. public enum Gender {
  2. MALE(0,"男"),FEMALE(1,"女"),UNKOWN(2,"未知");
  3. private Integer id;
  4. private String name;
  5. private Gender(Integer id,String name) {
  6. this.id = id;
  7. this.name = name;
  8. }
  9. private Gender(Integer id) {
  10. this.id = id;
  11. }
  12. private Gender(String name) {
  13. this.name = name;
  14. }
  15. public String getName() {
  16. return name;
  17. }
  18. public void setName(String name) {
  19. this.name = name;
  20. }
  21. @JsonValue
  22. public Integer getId() {
  23. return id;
  24. }
  25. public void setId(Integer id) {
  26. this.id = id;
  27. }
  28. }

把枚举类型保存到数据库中

MyBatis内置了两个枚举转换器分别是:org.apache.ibatis.type.EnumTypeHandler和org.apache.ibatis.type.EnumOrdinalTypeHandler。

  • EnumTypeHandler
    这是默认的枚举转换器,该转换器将枚举实例转换为实例名称的字符串,即将SexEnum.MALE转换MALE。
  • EnumOrdinalTypeHandler
    顾名思义这个转换器将枚举实例的ordinal属性作为取值,即SexEnum.MALE转换为0,SexEnum.FEMALE转换为1。

使用时,修改mapper的xml文件:

  1. <result column="status" property="orderStatusEnum" typeHandler="org.apache.ibatis.type.EnumOrdinalTypeHandler"/>
  2. <insert id="insert" parameterType="com.example.entity.OrderInfo">
  3. INSERT INTO
  4. order_test
  5. (status)
  6. VALUES (
  7. #{orderStatusEnum, typeHandler=com.example.typeHandler.EnumOrderStatusHandler, jdbcType=INTEGER}
  8. )
  9. </insert>

当然,也可以自定义枚举类型转换器,这里不详述。

注意:
保存枚举的ordinal值时,给枚举增加取值要加在最后,在中间插值会导致数据前后不一致。

发表评论

表情:
评论列表 (有 0 条评论,644人围观)

还没有评论,来说两句吧...

相关阅读

    相关 (enum)

    前言 实际上,枚举类型是特殊的类,和C语言C++中的枚举不太一样,下面我们做详细说明。关于枚举类型有一个单独的设计模式:即单例设计模式。单例类是一个类只有一个实例,那么多

    相关 enum

    枚举类型enum C++中,枚举类型(enumeration) 是一个被命名的整型常量的集合。和类一样,每个枚举类型定义了一种新的类型。枚举属于字面值常量类型。C++包含