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去继承通用转换器, 从而简化了代码.