Java中使用枚举(enum)还是常量?枚举!
这里先说结论:对于一组关联的数值,出于对数据安全的考虑,我们选择使用enum。问题定义表结构的时候经常会碰到一类字段:状态 ( status 或者 state ) 、类型 ( type ) ,而通常的做法一般是:数据库 中定义 tinyint 类型。比如:status tinyint(1) NOT NULL COMMENT ‘订单状态 1-待支付;2-待发货;3-待收货;4-已收货;5-已完结;’
这里先说结论:对于一组关联的数值,出于对数据安全的考虑,我们选择使用enum。
问题
定义表结构的时候经常会碰到一类字段:状态 ( status 或者 state ) 、类型 ( type ) ,而通常的做法一般是:
- 数据库 中定义 tinyint 类型。
比如:status tinyint(1) NOT NULL COMMENT ‘订单状态 1-待支付;2-待发货;3-待收货;4-已收货;5-已完结;’ - Java 实体类 中定义 Short 类型。
比如:private Short status
然后项目中可能会充斥着下面这样的代码:
order.setStatus((short) 1);
if (order.getStatus() == 1) {
order.setStatus((short) 2);
}
if (order.getStatus() == 4) {
order.setStatusName("已收货");
}
这都是些什么魔鬼数字啊,没有注释根本没法看,如果手滑可能状态就设错了,而且不好排查是在哪处赋值的。
改进方案是用 常量 ,但是又会产生另一种效果:
public static final Short WAIT_PAY = 1;
if (WAIT_PAY.equals(order.getStatus())) {
// 混用了解下
order.setStatus((short) 2);
}
定义常量对使用者没有约束力,仍然可能会被程序员无视,而直接使用数字。
这时候就该 枚举 出场了,枚举 的本质就是 类 + 常量 ,可以使用 枚举 来定义 一组 相关的元数据 ( 值、描述及其他必要信息 ) ,使用 枚举 类型不仅减小了数据维护 ( 比如调整了值的定义 ) 的成本,还加强了代码的 约束力 。
我们在实体类(DO/DTO/BO/VO/PO)和方法参数中,统一使用枚举类型的属性来保存有固定取值的字段。
要做到上面这一点,有下面三个地方要注意
- 把接口的参数转化为枚举类型
- 把接口返回的枚举类型转为String
- 把枚举类型保存到数据库中
把接口的参数转化为枚举类型
Spring 默认使用Bean接收枚举参数时支持 字面量,这也是我们常见的做法。
如下:
@Data
public class UserCommand {
private String name;
private Gender gender;
private String email;
}
@ApiOperation("添加用户")
@PostMapping("/users")
public User users(User command){
User user = new User();
BeanUtils.copyProperties(command,user);
return user;
}
注意这种方式不支持枚举的ordinal值
把接口返回的枚举类型转为String
《阿里巴巴Java开发手册》将接口中枚举的使用分为两类,即 接口参数和接口返回值,并规定: 接口参数可以使用枚举类型,但接口返回值不可以使用枚举类型(包括含枚举类型的POJO对象)。
这个主要是对dubbo等使用tcp协议的rpc接口调用来说的,为了避免序列化异常;对于spring cloud等使用http协议的框架,没有此限制。
jackson 解析枚举 时,默认会返回字面量( MALE,FEMALE)。也可以通过@JsonValue指定要返回的属性:
public enum Gender {
MALE(0,"男"),FEMALE(1,"女"),UNKOWN(2,"未知");
private Integer id;
private String name;
private Gender(Integer id,String name) {
this.id = id;
this.name = name;
}
private Gender(Integer id) {
this.id = id;
}
private Gender(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@JsonValue
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
}
把枚举类型保存到数据库中
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文件:
<result column="status" property="orderStatusEnum"
typeHandler="org.apache.ibatis.type.EnumOrdinalTypeHandler"/>
<insert id="insert" parameterType="com.example.entity.OrderInfo">
INSERT INTO
order_test
(status)
VALUES (
#{orderStatusEnum, typeHandler=com.example.typeHandler.EnumOrderStatusHandler, jdbcType=INTEGER}
)
</insert>
当然,也可以自定义枚举类型转换器,这里不详述。
注意:
保存枚举的ordinal值时,给枚举增加取值要加在最后,在中间插值会导致数据前后不一致。
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)