【发布时间】:2014-07-31 05:58:12
【问题描述】:
我们正在尝试找出一种使用 JPA 持久化枚举的稳健方法。使用@Enumerated 的常用方法是不可取的,因为重构时很容易破坏映射。每个枚举都应该有一个单独的数据库值,该值可以不同于枚举名称/顺序,以便您可以安全地更改枚举的名称或内部顺序(例如序数值)而不会破坏任何内容。例如。 this blog post 有一个关于如何实现这一点的示例,但我们觉得建议的解决方案给代码增加了太多混乱。我们希望通过使用 JPA 2.1 中引入的新 AttributeConverter 机制来实现类似的结果。我们有一个每个枚举都应该实现的接口,该接口定义了一个获取用于将枚举存储在数据库中的值的方法。示例:
public interface PersistableEnum {
String getDatabaseValue();
}
...
public enum SomeEnum implements PersistableEnum {
FOO("foo"), BAR("bar");
private String databaseValue;
private SomeEnum(String databaseValue) {
this.databaseValue = databaseValue;
}
public void getDatabaseValue() {
return databaseValue;
}
}
我们还有一个基本转换器,它具有将枚举转换为字符串的逻辑,反之亦然,并为每种枚举类型单独的具体转换器类(AFAIK,不可能实现完全通用的枚举转换器,这也注意到@ 987654322@)。然后,具体的转换器只需调用进行转换的基类,如下所示:
public abstract class EnumConverter<E extends PersistableEnum> {
protected String toDatabaseValue(E value) {
// Do the conversion...
}
protected E toEntityAttribute(Class<E> enumClass, String value) {
// Do the conversion...
}
}
...
@Converter(autoApply = true)
public class SomeEnumConverter extends EnumConverter<SomeEnum>
implements AttributeConverter<SomeEnum, String> {
public String convertToDatabaseColumn(SomeEnum attribute) {
return toDatabaseValue(attribute);
}
public SomeEnum convertToEntityAttribute(String dbData) {
return toEntityAttribute(SomeEnum.class, dbData);
}
}
然而,虽然这种方法在技术意义上非常有效,但仍然存在一个非常严重的缺陷:每当有人创建一个新的枚举类,其值需要存储到数据库中时,该人还需要记住创建新的枚举类enum 实现PersistableEnum 接口并为其编写转换器类。没有这个,枚举将毫无问题地持久化,但转换将默认使用@Enumerated(EnumType.ORDINAL),这正是我们想要避免的。我们怎样才能防止这种情况发生?有没有办法让 JPA(在我们的例子中是 Hibernate)不默认为任何映射,但是例如如果没有在字段上定义 @Enumerated 并且找不到该类型的转换器,则抛出异常?或者我们可以创建一个“包罗万象”的转换器,为所有没有自己特定转换器类的枚举调用,并且总是从那里抛出异常?还是我们只能忍气吞声,每次都尝试记住额外的步骤?
【问题讨论】:
-
下面的链接中有一种方法。但是,它并没有解决您提到的陷阱,但至少它提供了一种很好的通用方法来处理枚举:javacodebox.blogspot.com.au/2013/12/…