JPA 中@Enumerated


在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
}

這里Gender就是我們的Enum類,如下:

public enum Gender {
    male("男"),
    female("女");
 
    private String name;
    private Gender(String name){
        this.name = name;
    }
 
    public String getName(){
        return this.name;
    }
}

此時看到生成的表結構如下: gender  int

這里可以看出Enum類型字段Gender**被映射成int(11)類型**,通過往里面插入一條數據看一下結果,如下

@Test
public void testJpa(){
    personService.save(new Person("Jhon", Gender.male));
    personService.save(new Person("Lily", Gender.female));
}

上邊向數據庫中插入了兩條記錄,personService是操作數據庫的service,結果如下:

可以看出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.

 @Enumerated注解定義:

@Target({METHOD, FIELD})
@Retention(RUNTIME)
public @interface Enumerated {

    /** (Optional) The type used in mapping an enum type. */
    EnumType value() default ORDINAL;
}

EnumType枚舉類型定義:

package javax.persistence;

/**
 * Defines mapping for enumerated types.  The constants of this
 * enumerated type specify how a persistent property or
 * field of an enumerated type should be persisted.
 *
 * @since Java Persistence 1.0
 */
public enum EnumType {
    /** Persist enumerated type property or field as an integer. */
    ORDINAL,

    /** Persist enumerated type property or field as a string. */
    STRING
}

如果不使用該注解,就是在上面說的默認的情況是使用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
}

此時看到生成的表結構如下: gender varchar

這里可以看出Enum類型字段Gender**被映射成VARCHAR(255)類型**,通過往里面插入一條數據看一下結果,如下:

@Test
public void testJpa(){
    personService.save(new Person("Jhon", Gender.male));
    personService.save(new Person("Lily", Gender.female));
}

可以看出是插入的對應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);
    }
}

所有的屬性轉換器需要實現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;
    }
}

下面使用這個轉換器如下:

@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
}

生成的數據庫結構如下:

 

 

 可以看出和使用@Enumerated(EnumType.STRING)生成的類型是相同的,但是別急,看看插入的數據是怎么樣的?

@Test
public void testJpa(){
    personService.save(new Person("Jhon", Gender.male));
    personService.save(new Person("Lily", Gender.female));
}

結果如下:

 

 可以看出插進去的數據已經不是Enum實例的name屬性值了,而是我們自定義的value值。

查詢:

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM