【问题标题】:How to map enums that share interface to entity's column with JPA?如何使用 JPA 将共享接口的枚举映射到实体的列?
【发布时间】:2023-10-24 03:36:07
【问题描述】:
public interface Base { /* anything */ }
public enum A implements Base { /* anything */ }
public enum B implements Base { /* anything */ }

@Entity
public class Clazz 
{
    @Column(nullable = false)
    private Base base;
    ...
}

我收到此错误:

Fields "base" are not a default persistent type,
 and do not have any annotations indicating their persistence strategy. They will be treated as non-persistent. If you i
ntended for these fields to be persistent, be sure to annotate them appropriately or declare them in orm.xml. Otherwise 
annotate them with @Transient.

我不能从那个接口创建抽象类,因为枚举不允许继承。

有人知道一些解决方法或我有什么选择吗?

【问题讨论】:

    标签: jpa enums jpa-2.0 openjpa


    【解决方案1】:

    简答

    当您尝试映射接口类型Base 和持久性提供程序(此处为:OpenJPA)时,没有应用于将此相应字段映射到的特定类型的具体信息,这在您上面发布的场景中永远不会起作用。

    长答案

    在官方JPA 2.2 specification(第 26 页)的 2.2 Persistent Fields and Properties 部分中,我们发现:

    实体的持久字段或属性可能有以下类型:

    • Java 原始类型,

    • java.lang.String,

    • 其他 Java 可序列化类型(包括原始类型的包装器,

      java.math.BigInteger, java.math.BigDecimal, java.util.Date, java.util.Calendar[5]、java.sql.Date、java.sql.Time、java.sql.Timestamp、 字节[]、字节[]、字符[]、字符[]、java.time.LocalDate、java.time.Local- 时间,java.time.LocalDateTime,java.time.OffsetTime,java.time.OffsetDa- teTime,以及实现 Serializable 接口的用户定义类型);

    • 枚举;

    • 实体类型;

    • 实体类型的集合;

    • 可嵌入类(参见第 2.5 节);

    • 基本类型和可嵌入类型的集合(参见第 2.6 节)。

    (数据-)仅定义为interface 的类型,例如Base,不包括(或排除)在此处的上述列表中。因此,JPA 规范将任何 OR 映射器(又名持久性提供程序)限制为定义明确的具体可序列化类型,或上述类型的集合。

    注意,Base 类型的属性base 显然不是枚举本身。

    想法

    • (1) 考虑一个常见的枚举类型,例如CommonEnum,它包含AB 的所有枚举值以及其中的一个额外字段,用于区分枚举类型,或者说,category。这可以通过内部枚举Category 实现,其值为EnumAEnumB,将不同类型分开以供以后区分。使用特定的构造函数CommonEnum(value, category) 来初始化CommonEnum 的每个实例。根据需要添加 getter/setter。

    • (2) 鉴于您遵循了 (1) 的想法,您可以通过 @Enumerated 进行注释(请参阅 JPA 规范的第 11.1.18 节):

      @Entity
      public class Clazz 
      {
          @Enumerated(STRING)
          private CommonEnum commonEnum;
      
    • (3) 如果 (1) + (2) 对您不利,请考虑

      @Entity
      public class Clazz 
      {
          @Enumerated(STRING)
          private A aEnum;
      
          @Enumerated(STRING)
          private B bEnum;
      

      作为一个解决方案的想法。可能不是很优雅,但它对于 OR 映射器来说足够具体,因此可以持久化和检索。要指示未设置的值(对于AB,您可能希望在两个枚举(AB)中添加UNSET。鉴于这种方法,您还可以查看@PrePersist(请参阅第 3.5.3 节,第 101 页)在持久化到关联数据库之前解析字段 aEnumbEnum(或什至同时)中的潜在 null 值。

    【讨论】:

      最近更新 更多