Hibernate枚举类型的映射

目录
  1. 基本 - 按枚举的ordinal属性映射
  2. 基本 - 按枚举的name属性映射
  3. 灵活映射 - 使用Hibernate自定义类型
  4. 灵活映射 - 使用AttributeConverter

Hibernate枚举类型的映射

本文介绍一下Hibernate映射枚举类型的几种方式.

首先定义一个枚举.

1
2
3
4
5
6
7
8
9
10
11
12
13
public enum Gender {
male("男"),
female("女");

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

public char getValue() {
return name;
}
}

基本 - 按枚举的ordinal属性映射

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Entity
public class Person {

@Id
@GeneratedValue
private Long id;

@Column
private String name;

// 没有额外注解 或 添加注解 @Enumerated(EnumType.ORDINAL)
@Column
// @Enumerated(EnumType.ORDINAL) // 可省略
private Gender gender;

// ...
}

按照这种写法, Hibernate会将Enum类型在声明中的顺序即ordinal属性映射成int型存入数据库, 对于枚举Gender来说, male在数据库中存的是 0, 而female是 1.
好处是简单, 缺点是枚举值多时可读性差, 若声明顺序不小心发生变化映射会出错且不易发现.

基本 - 按枚举的name属性映射

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Entity
public class Person {

@Id
@GeneratedValue
private Long id;

@Column
private String name;

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

// ...
}

使用此方法数据库中存储的是枚举类型的name属性, 即male在数据库中存的就是’male’, female在数据库中存的就是’female’.
好处是同样也比较简单,可读性大于第一种

附EnumType源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
* 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
}

灵活映射 - 使用Hibernate自定义类型

自定义类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class GenderType extends AbstractSingleColumnStandardBasicType<Gender> {

public static final GenderType INSTANCE = new GenderType();

public GenderType() {
super(
CharTypeDescriptor.INSTANCE,
GenderJavaTypeDescriptor.INSTANCE
);
}

public String getName() {
return "gender";
}

@Override
protected boolean registerUnderJavaType() {
return true;
}
}
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
26
27
28
29
30
31
public class GenderJavaTypeDescriptor extends AbstractTypeDescriptor<Gender> {

public static final GenderJavaTypeDescriptor INSTANCE =
new GenderJavaTypeDescriptor();

protected GenderJavaTypeDescriptor() {
super( Gender.class );
}

public String toString(Gender value) {
return value == null ? null : value.name();
}

public Gender fromString(String string) {
return string == null ? null : Gender.valueOf( string );
}

public <X> X unwrap(Gender value, Class<X> type, WrapperOptions options) {
return CharacterTypeDescriptor.INSTANCE.unwrap(
value == null ? null : value.getValue(),
type,
options
);
}

public <X> Gender wrap(X value, WrapperOptions options) {
return Gender.fromString(
CharacterTypeDescriptor.INSTANCE.wrap( value, options )
);
}
}

使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Entity(name = "Person")
public static class Person {

@Id
@GeneratedValue
private Long id;

@Column
private String name;

@Column
@Type( type = "org.hibernate.userguide.mapping.basic.GenderType" )
public Gender gender;

// ...

}

灵活映射 - 使用AttributeConverter

AttributeConverter可用于各种类型的自定义映射, 当然也包括了枚举类型.
AttributeConverter<X, Y>, 其中 X 是实体属性的类型, Y 是数据库字段的类型.

示例:
定义转换器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class GenderConverter implements AttributeConverter<Gender, String> {

// 将实体属性X转化为Y存储到数据库中
@Override
public String convertToDatabaseColumn(Gender attribute) {
if (attribute == null) {
return null;
}
return attribute.getValue();
}

// 将数据库中的字段Y转化为实体属性X
@Override
public Gender convertToEntityAttribute(String dbData) {
return Gender.fromString(dbData);
}
}

完善枚举类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public enum Gender {
male("男"),
female("女");

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

public static Gender fromString(String value) {
Gender gender = null;
switch(value): {
case "男":
gender = male;
break;
case "女":
gender = female;
break;
}
return gender;
}
}

使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Entity
public class Person {

@Id
@GeneratedValue
private Long id;

@Column(nullable = false)
private String name;

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

// ...
}

属性转换器需要实现AttributeConverter接口,该接口可以完成实体中数据和数据库中数据的相互转换.

此时在数据库中对应存入的值为’男’,’女’

在实际项目中使用的枚举类型可能又有相似的地方,比如male(“男”), 有一个字符串参数, 需要使用该参数进行映射. 若有若干相似枚举类型, 每个枚举都写属性转换器太过麻烦. 此时可以定义通用接口, 相关枚举类型实现该接口, 然后将转换器AttributeConverter<X, Y>中的X设为该接口. 对应枚举再定义static class去继承通用转换器, 从而简化了代码.