JPA 枚举类型映射。最佳方法

2022-09-01 07:25:19

像往常一样,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)

在我的表中,我希望有指向 myEnums 表的字段。enumHoldermyEnumCode

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

还有另一个问题。如何使用 Java 类数据填写表格?你会怎么做?请采取最好的方法。myEnumsMyEnum

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

让我们假设有一个带有 和 name 的表。Java枚举,这在问题中有所描述。 表需要具有对字段的引用。如果您不同意,请评论解决方案。myEnumcodefieldsMyEnumenumHoldermyEnumCodemyEnum.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 的替代方案请不要提供。我不想在这里写下使用它时可能存在的所有问题(在有效的Java中,它被描述为关于序数的使用)。使用EnumType.STRING我不喜欢任何一个,因为它不允许在数据库中有描述并从数据库请求它。

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


答案 1

最好的方法是将唯一的 ID 映射到每个枚举类型,从而避免 ORDINAL 和 STRING 的陷阱。请参阅这篇文章,其中概述了可以映射枚举的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)

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

有关代码示例,请参阅原始帖子


答案 2

可以使用枚举字段添加到实体@Enumerated。这里的踢球者是你想吃你的蛋糕和吃它 - 为了一致性的缘故,最好选择任何一个或。EnumType.ORDINALEnumType.STRING

两者都有其优点和缺点,在此站点上列出。最大的区别似乎在于排序 - 如果您已经设置,则在更新枚举时会遇到问题。EnumType.ORDINAL

我鼓励你重新思考你想要设计桌子的方式。字符串和整数实质上存储相同的信息( 枚举值是什么 ) 并且您可以从数据库中取回该信息,而无需大惊小怪。最好在表上有一个作为主键,然后将枚举类型作为字符串或序号值,同时考虑到上面链接中的警告。id