强制使用JPA AttributeConverter作为枚举

时间:2014-06-10 12:55:04

标签: java hibernate jpa enums

我们正试图找出一种使用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,一个完全通用的枚举转换器是不可能实现的,这也是注意到的{ {3}})。然后,具体转换器只需调用执行转换的基类,如下所示:

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);
    }
}

然而,虽然这种方法在技术意义上非常好用,但仍有一个相当令人讨厌的陷阱:每当有人创建一个新的枚举类,其值需要存储到数据库时,该人还需要记住制作新的枚举实现PersistableEnum接口并为其编写转换器类。如果没有这个,枚举将持续存在而没有问题,但转换将默认使用@Enumerated(EnumType.ORDINAL),这正是我们想要避免的。我们怎么能阻止这个?有没有办法让JPA(在我们的例子中,Hibernate)不默认任何映射,但是例如如果在字段上没有定义@Enumerated并且找不到该类型的转换器,则抛出异常?或者我们可以创建一个“全部捕获”转换器,为所有没有自己的特定转换器类的枚举调用,并始终从那里抛出异常?或者我们是否只需要吸吮它并尝试每次都记住其他步骤?

1 个答案:

答案 0 :(得分:0)

您要确保所有枚举是PersistableEnum的实例。

您需要设置默认实体监听器(一个实体监听器,其回调适用于持久性单元中的所有实体)。

默认实体侦听器类中,实现 @PrePersist 方法,并确保所有枚举都是PersistableEnum的实例。