前言
今天做测试,发现还没有试过实体类中关于枚举项的使用,于是就做了个测试,发现了点问题(发现的问题主要是针对我使用的ORM框架是Ebean,它自带了一个比@Enumerated更好用的注解和方式)。
不多说,直接进行说明 。。。
@Enumerated的使用测试
比如我有一张表,有一个字段是性别gender,这个字段我只想设置男女,于是我就想到了枚举,建表如下:
CREATE TABLE `j_test2` ( -- 忽略这里的表名,只是为了测试
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(30) DEFAULT NULL,
`gender` int(10) DEFAULT NULL, -- 注意这里性别使用了int类型,和@Enumerated会有关系
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
首先我们要明确的是将Enum类型的字段映射到数据库中有两种方式:
- 通过使用Enum类型实例在Enum中声明的顺序,也就是ordinal属性,通过这个序号来将Enum类型字段映射成int类型来存储。
- 通过使用Enum类型实例中的name属性来完成映射,这里讲Enum类型映射成String类型来完成存储。
定义的枚举如下:
package demo.springboot.cons;
/**
* @author jiangkd
* @date 2022/12/13 14:35:58
*/
public enum Gender {
/**
* 男
* ordinal是0, name是MALE
*/
MALE,
/**
* 女
* ordinal是1, name是FEMALE
*/
FEMAL
}
我们的实体类如下:
package demo.springboot.entity;
import demo.springboot.cons.Gender;
import lombok.*;
import lombok.experimental.Accessors;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Table;
/**
* @author j
* @date 2022/12/13 14:34:45
*/
@Accessors(chain = true)
@Getter
@Setter
@ToString
@AllArgsConstructor
@NoArgsConstructor
@Entity
// ORM使用的是Ebean,不是Mybatis,实体类会有所不同
@Table(name = "j_test2")
public class Jtest2 {
private Integer id;
private String name;
/**
* 性别使用了枚举
*/
@Column(name = "gender")
private Gender gender;
}
1.1.不使用注解@Enumerated
其实,不使用注解,默认的就是@Enumerated(EnumType.ORDINAL),也就是实体类gender属性改为如下:
@Enumerated(EnumType.ORDINAL)
@Column(name = "gender")
private Gender gender;
测试插入两条数据:
package demo.springboot.enum_;
import demo.springboot.DemoSpringbootApplication;
import demo.springboot.cons.Gender;
import demo.springboot.entity.Jtest2;
import io.ebean.Database;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
/**
* @author j
* @date 2022/12/13 14:42:51
*/
@SpringBootTest(classes = DemoSpringbootApplication.class)
@RunWith(SpringRunner.class)
@Slf4j
public class Jtest2Test {
@Autowired
Database database;
@Test
public void add() {
//
final Jtest2 jtest = new Jtest2()
.setName("test1")
// 设置枚举MALE
.setGender(Gender.MALE);
// 保存到数据库
database.save(jtest);
final Jtest2 jtest2 = new Jtest2()
.setName("test2")
// 设置枚举FEMALE
.setGender(Gender.FEMAL);
// 保存到数据库
database.save(jtest2);
}
}
查看数据库:
可以看出,插入数据的时候,gender列对应的就是枚举项的ordinal序号,第一个MALE就是0,第二个FEMALE是1。这样就存在一个问题,如果我们又添加了一个OTHER的枚举项,放在了MALE后面,此时他们的ordinal序号就变了(MALE是0,OTHER是1,FEMALE就成了2了),这样的已经存入数据库的gender数据就和枚举项不对应了,容易出现问题。
所以我们推荐使用下面的方式,不存储枚举项的ordinal了,而是它的name。
1.2.使用@Enumerated(EnumType.STRING)
我们的枚举不动,依然是只有MALE和FEMALE,只是修改实体类中gender属性,添加注解@Enumerated(EnumType.STRING),如下:
@Enumerated(EnumType.STRING)
@Column(name = "gender")
private Gender gender;
注意,一开始我们创建j_test2表的时候,gender属性我们使用的int类型,就是为了第一种方式存储枚举项的ordinal序号,而现在我们要存在的是枚举项的name(MALE,FEMALE),所所以数据库的gender类型要改为varchar类型,如下:
alter table j_test2 modify column gender varchar(10) null comment '性别';
truncate table j_test2; -- 清空数据, 便于测试查看
我们再次执行上面Jtest2Test的add方法 。。。
再次查看数据库:
咋样,此时gender存储的是枚举项的name了,这样就算枚举项添加,删除(ordinal变化)也没事了,因为我们存储的是name。
总结
虽然以上的使用没有什么问题,但有没有觉得怪怪的。
首先使用@Enumerated(EnumType.ORDINAL)的方式,就存在修改枚举项导致ordinal变化的问题。
其次使用@Enumerated(EnumType.STRING)的方式,也并没有达到我想要的效果,它只是存储了name,我想要的是数据库存储枚举项的构造参数值,查询或保存的时候使用的枚举项的name。(肯定有方式,但是我不想特殊处理一下之类的,就是直接使用即可那种)
我用的ORM框架式Ebean,发现Ebean中有个注解是@DbEnumValue,确实是我需要的。