欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

JPA 中@Enumerated

程序员文章站 2022-04-23 16:53:20
...

在Java中Enum是一种“奇葩”的存在,“奇葩”不代表没用,对于这种比较特殊的存在,hibernate会有很多种选择来完成Enum类型字段的映射,首先要说明的是在hibernate都是把Enum类型的字段映射成基本类型的字段,并且我这里不使用任何配置文件来配置映射,而是统一使用注解这种方式来完成映射。

注意本文使用的数据库是MySQL5.7,版本和数据库类型可能会有不同的结果。

首先我们要明确的是将Enum类型的字段映射到数据库中有两种方式:

  1. 一个是通过使用Enum类型实例在Enum中声明的顺序,也就是ordinal属性,通过这个序号来将Enum类型字段映射成int类型来存储;
  2. 一个是通过使用Enum类型实例中的name属性来完成映射,这里讲Enum类型映射成String类型来完成存储;

这两个属性其实都在java.lang.Enum中,这个类是所有Enum类型的父类。

1. 不使用任何注解

当不使用任何注解的时候,默认情况下是使用ordinal属性,也就是Enum类型实例在Enum中声明的顺序来完成映射的,具体情况如下:

@Entity
public class Person implements Serializable{
    private static final long serialVersionUID = 8849870114127659929L;

    @Id
    @GeneratedValue
    private Long id;

    @Column(nullable = false)
    private String name;

    @Column
    private Gender gender;

    public Person(String name, Gender gender){
        this.name = name;
        this.gender = gender;
    }

    // getter、setter
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

这里Gender就是我们的Enum类,如下:

public enum Gender {
    male("男"),
    female("女");

    private String name;
    private Gender(String name){
        this.name = name;
    }

    public String getName(){
        return this.name;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

此时看到生成的表结构如下:

JPA 中@Enumerated

这里可以看出Enum类型字段Gender**被映射成int(11)类型**,通过往里面插入一条数据看一下结果,如下:

@Test
public void testJpa(){
    personService.save(new Person("Jhon", Gender.male));
    personService.save(new Person("Lily", Gender.female));
}
  • 1
  • 2
  • 3
  • 4
  • 5

上边向数据库中插入了两条记录,personService是操作数据库的service,结果如下:

JPA 中@Enumerated

可以看出Gender.male对应的序号是0,Gender.female对应的是1,可以看出在使用ordinal属性映射时的序号是从0开始的

2. 使用@Enumerated注解

注解javax.persistence.Enumerated从名字上来看就可以明白是为了映射Enum类型的,从它的javadoc文档中可以说明:

Specifies that a persistent property or field should be persisted as a enumerated type. The Enumerated annotation may be used in conjunction with the Basic annotation, or in conjunction with the ElementCollection annotation when the element collection value is of basic type. If the enumerated type is not specified or the Enumerated annotation is not used, the EnumType value is assumed to be ORDINAL.

如果不使用该注解,就是在上面说的默认的情况是使用Enum中的ordinal属性来完成,所以关于这部分就不详细阐述了。

主要说明一下将Enum类型映射成字符串的方式,其实只是将Enum类型上的注解改为@Enumerated(EnumType.STRING)即可,如下:

@Entity
public class Person implements Serializable{
    private static final long serialVersionUID = 8849870114127659929L;

    @Id
    @GeneratedValue
    private Long id;

    @Column(nullable = false)
    private String name;

    @Column
    @Enumerated(EnumType.STRING)
    private Gender gender;

    public Person(String name, Gender gender){
        this.name = name;
        this.gender = gender;
    }

    // getter、setter
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

此时看到生成的表结构如下:

JPA 中@Enumerated

这里可以看出Enum类型字段Gender**被映射成VARCHAR(255)类型**,通过往里面插入一条数据看一下结果,如下:

@Test
public void testJpa(){
    personService.save(new Person("Jhon", Gender.male));
    personService.save(new Person("Lily", Gender.female));
}
  • 1
  • 2
  • 3
  • 4
  • 5

JPA 中@Enumerated

可以看出是插入的对应Enum类型的name属性,就是这么简单!

3. 使用AttributeConverter属性类型转换器

关于AttributeConverter<X, Y>

  • X 是实体属性的类型
  • Y 是数据库字段的类型
  • Y convertToDatabaseColumn(X) 作用:将实体属性X转化为Y存储到数据库中,即插入和更新操作时执行;
  • X convertToEntityAttribute(Y) 作用:将数据库中的字段Y转化为实体属性X,即查询操作时执行;

使用AttributeConverter可以将Enum中的属性名转换成数据库中存储的字段名,定义一个属性转换器如下:

@Converter
public class GenderConverter implements AttributeConverter<Gender, String> {

    @Override
    public String convertToDatabaseColumn(Gender attribute) {
        return attribute.getValue();
    }

    @Override
    public Gender convertToEntityAttribute(String dbData) {
        return Gender.fromString(dbData);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

所有的属性转换器需要实现AttributeConverter接口,这个接口可以将实体中的数据转换成到数据库中存储,这种方式可以用来映射实体属性,也可以用来将实体中数据加密存储到数据库中,这个转换器还需要使用@Converter注解标识出来。

下面要重写我们的Gender枚举类,如下:

public enum Gender {
    male("男"),
    female("女");

    private String value;
    private Gender(String value){
        this.value = value;
    }

    public String getValue(){
        return this.value;
    }

    public static Gender fromString(String value){
        Objects.requireNonNull(value, "value can not be null");
        Gender gender = null;
        if("男".equals(value)){
            gender = male;
        }
        else if("女".equals(value)){
            gender = female;
        }
        return gender;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

下面使用这个转换器如下:

@Entity
public class Person implements Serializable{
    private static final long serialVersionUID = 8849870114127659929L;

    @Id
    @GeneratedValue
    private Long id;

    @Column(nullable = false)
    private String name;

    @Column
    @Convert(converter = GenderConverter.class)
    private Gender gender;

    public Person(String name, Gender gender){
        this.name = name;
        this.gender = gender;
    }

    // getter、setter
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

生成的数据库结构如下:

JPA 中@Enumerated

可以看出和使用@Enumerated(EnumType.STRING)生成的类型是相同的,但是别急,看看插入的数据是怎么样的?

@Test
public void testJpa(){
    personService.save(new Person("Jhon", Gender.male));
    personService.save(new Person("Lily", Gender.female));
}
  • 1
  • 2
  • 3
  • 4
  • 5

结果如下:

JPA 中@Enumerated

可以看出插进去的数据已经不是Enum实例的name属性值了,而是我们自定义的value值。