JPA枚举类型映射。最好的方法

时间:2013-04-22 05:53:24

标签: java jpa enums hibernate-mapping

通常,Java枚举类型具有相应的代码和名称描述。 包含此类字段的Java类将它们包含为Enum:

public enum MyEnum{
    SOMEINSTANCE(1, "test1"),
    SOMEINSTANCE(2, "test2");

    private final int code;
    private final String name;
    private MyEnum(int code, String name){
        this.code = code;
        this.name = name;
    }
    ... helper getter for code and name
}

@Entity
puclic class EnumHolder{
    private MyEnum myEnum;
}

我是JPA的新手,但我想拥有'myEnums'表,看起来像:

code int not null, name varchar(50) not null)

在我的enumHolder表中,我希望myEnumCode字段指向myEnums表。

使用currenlty支持EnumType.ORDINAL和EnumType.STRING我认为不是一个好主意。

另一个问题。如何使用Java myEnums类数据填写MyEnum表?你会怎么做?请最好的方法。

PS:以下是我可以提供的解决方案:

我们假设有myEnumcode和名称fields。 Java MyEnum枚举,在问题中描述。 enumHolder表需要myEnumCode引用myEnum.code字段。如果您不同意,请评论解决方案。

@Entity
@Access(AccessType.FIELD)
public class EnumHolder {
    @Id private int id;
    @Transient private MyEnum myEnum;
    …
    public int getId() { return id; }
    public void setId(int id) { this.id = id; }
    public MyEnum getMyEnum() { return MyEnum; }
    public void setMyEnum(MyEnum myEnum) { this.myEnum = myEnum; }

    @Access(AccessType.PROPERTY) @Column(name="myEnumCode")
    protected int getMyEnumForDb() {
        return myEnum.getCode();
    }

    protected void setMyEnumForDb(int enumCode) {
        myEnum = MyEnum.getByCode( enumCode);
    }
…
}

当然这里有缺点。但目前我看不到更好的方法。 EnumType.ORDINAL和EnumType.STRING的替代方案请不要提供。我不想在这里写出其使用中可能存在的所有问题(在Effective Java中,它描述了有关序数的用法)。使用EnumType.STRING我不喜欢它,因为它不允许在数据库中有描述并从db请求它。

关于fillind数据库。我认为编写一个脚本并不困难,它会清除myEnum表,然后对每个Java枚举表进行插入。并且始终在部署阶段执行此操作。

3 个答案:

答案 0 :(得分:48)

最好的方法是将唯一ID映射到每个枚举类型,从而避免ORDINAL和STRING的陷阱。请参阅此post,其中概述了映射枚举的5种方法。

取自上面的链接:

<强> 1和2。使用@Enumerated

目前有两种方法可以使用@Enumerated注释在JPA实体中映射枚举。不幸的是,EnumType.STRING和EnumType.ORDINAL都有其局限性。

如果使用EnumType.String,则重命名其中一个枚举类型将导致枚举值与数据库中保存的值不同步。如果使用EnumType.ORDINAL,则删除或重新排序枚举中的类型将导致数据库中保存的值映射到错误的枚举类型。

这两个选项都很脆弱。如果在不执行数据库迁移的情况下修改枚举,则可能会破坏数据的完整性。

第3。生命周期回调

可能的解决方案是使用JPA生命周期回调注释,@ PrePersist和@PostLoad。这感觉非常难看,因为您现在在实体中有两个变量。一个映射存储在数据库中的值,另一个映射实际的枚举。

<强> 4。将唯一ID映射到每个枚举类型

首选解决方案是将枚举映射到枚举中定义的固定值或ID。映射到预定义的固定值使您的代码更加健壮。对枚举类型顺序的任何修改或名称的重构都不会造成任何不利影响。

<强> 5。使用Java EE7 @Convert

如果您使用的是JPA 2.1,则可以选择使用新的@Convert注释。这需要创建一个使用@Converter注释的转换器类,您可以在其中定义为每个枚举类型保存到数据库中的值。在您的实体中,您将使用@Convert注释您的枚举。

我的偏好:(第4名)

我更喜欢在enum中定义我的ID而不是使用转换器,这是一个很好的封装。只有枚举类型应该知道它的ID,并且只有实体应该知道它如何将枚举映射到数据库。

请参阅原始post以获取代码示例。

答案 1 :(得分:12)

您可以使用@Enumerated向您的实体添加枚举字段。这里的踢球者是你想吃蛋糕并吃它 - 为了保持一致,最好选择EnumType.ORDINALEnumType.STRING

两者都有自己的优点和缺点,列出at this site。最大的区别似乎在于订购 - 如果您设置了EnumType.ORDINAL,则在更新枚举时会遇到问题。

我建议您重新考虑设计表格的方式。字符串和整数本质上存储相同的信息 - 枚举值 - 你可以从数据库中获取它而不用太大惊小怪。最好在表格中使用id作为主键,然后将枚举类型作为字符串或序数值,同时考虑上面链接中的警告。

答案 2 :(得分:2)

@Column(name = "MY_TYPE")
@Enumerated(EnumType.STRING)
private MyType type;

enum MyType {
type_1
type_2
}

然后创建如下列的列:

TYPE_ID  VARCHAR(20) NOT NULL DEFAULT 'TYPE_1' CHECK (TYPE_ID IN ('TYPE_1', 'TYPE_2')))

这解决了我的问题。