【问题标题】:JPA map collection of Enums枚举的 JPA 映射集合
【发布时间】:2010-09-29 19:04:29
【问题描述】:

在 JPA 中有没有办法在 Entity 类中映射枚举集合?或者唯一的解决方案是用另一个域类包装 Enum 并使用它来映射集合?

@Entity
public class Person {
    public enum InterestsEnum {Books, Sport, etc...  }
    //@???
    Collection<InterestsEnum> interests;
}

我正在使用 Hibernate JPA 实现,但当然更喜欢实现无关的解决方案。

【问题讨论】:

    标签: java jpa jakarta-ee


    【解决方案1】:

    使用 Hibernate 你可以做到

    @CollectionOfElements(targetElement = InterestsEnum.class)
    @JoinTable(name = "tblInterests", joinColumns = @JoinColumn(name = "personID"))
    @Column(name = "interest", nullable = false)
    @Enumerated(EnumType.STRING)
    Collection<InterestsEnum> interests;
    

    【讨论】:

    • 如果有人现在读到这个...... @CollectionOfElements 现在已被弃用,而是使用:@ElementCollection
    • 你可以在这个问题的答案中找到一个示例:stackoverflow.com/q/3152787/363573
    • 既然你提到了 Hibernate,我认为这个答案可能是特定于供应商的,但我认为除非使用 JoinTable 在其他实现中引起问题,否则我认为不是。从我所见,我相信应该使用 CollectionTable。这就是我在回答中使用的,它对我有用(虽然是的,我现在也在使用 Hibernate。)
    • 我知道这是一个旧线程,但我们正在使用 javax.persistence 实现同样的事情。当我们添加时: @ElementCollection(targetClass=Roles.class) @CollectionTable( name="USER_ROLES", joinColumns=@JoinColumn(name="USER_ID") ) @Column( name="ROLE", nullable=false ) @Enumerated( EnumType.STRING) 私有 Set 角色;对于我们的 User 表,整个模型包中的东西都会脱落。在我们的 User 对象中,即使主键的 @Id...@GeneratedValue 生成器出现错误,第一个 @OneToMany 也会在构建时抛出愚蠢的错误。
    • 对于它的价值 - 我看到的错误是一个错误 - issues.jboss.org/browse/JBIDE-16016
    【解决方案2】:

    Andy 答案中的链接是在 JPA 2 中映射“非实体”对象集合的一个很好的起点,但在映射枚举方面并不完整。这是我想出的。

    @Entity
    public class Person {
        @ElementCollection(targetClass=InterestsEnum.class)
        @Enumerated(EnumType.STRING) // Possibly optional (I'm not sure) but defaults to ORDINAL.
        @CollectionTable(name="person_interest")
        @Column(name="interest") // Column name in person_interest
        Collection<InterestsEnum> interests;
    }
    

    【讨论】:

    • 可悲的是,一些“管理员”决定删除该答案而没有给出任何理由(这里的课程大约是标准杆)。作为参考,它是datanucleus.org/products/accessplatform_3_0/jpa/orm/…
    • 您真正需要的只是@ElementCollectionCollection&lt;InterestsEnum&gt; interests; 其余部分可能有用但没必要。例如@Enumerated(EnumType.STRING) 将人类可读的字符串放入您的数据库中。
    • 你是对的 - 在这个例子中,你可以依赖 @Columnname 被暗示。我只是想澄清省略@Column 时所暗示的内容。并且总是推荐使用@Enumerated,因为默认使用序数是一件糟糕的事情。 :)
    • 我认为值得一提的是,您实际上需要 person_interest 表
    • 我必须添加 joinColumn 参数才能使其工作@CollectionTable(name="person_interest", joinColumns = {@JoinColumn(name="person_id")})
    【解决方案3】:

    我能够以这种简单的方式完成此操作:

    @ElementCollection(fetch = FetchType.EAGER)
    Collection<InterestsEnum> interests;
    

    here 所述,需要预先加载以避免延迟加载初始化错误。

    【讨论】:

    【解决方案4】:

    tl;dr 一个简短的解决方案如下:

    @ElementCollection(targetClass = InterestsEnum.class)
    @CollectionTable
    @Enumerated(EnumType.STRING)
    Collection<InterestsEnum> interests;
    

    长答案是,使用此注释 JPA 将创建一个表,该表将保存指向主类标识符(在本例中为 Person.class)的 InterestsEnum 列表。

    @ElementCollections 指定 JPA 可以在哪里找到有关 Enum 的信息

    @CollectionTable 创建持有 Person 到 InterestsEnum 关系的表

    @Enumerated(EnumType.STRING) 告诉 JPA 将 Enum 持久化为字符串,可以是 EnumType.ORDINAL

    【讨论】:

    • 在这种情况下,我无法更改此集合,因为它保存为不可变的 PersistenceSet。
    • 我的错误。我们可以用 setter 来改变这个集合。
    【解决方案5】:

    我正在对 java.util.RegularEnumSet 稍作修改以获得持久的 EnumSet:

    @MappedSuperclass
    @Access(AccessType.FIELD)
    public class PersistentEnumSet<E extends Enum<E>> 
        extends AbstractSet<E> {
      private long elements;
    
      @Transient
      private final Class<E> elementType;
    
      @Transient
      private final E[] universe;
    
      public PersistentEnumSet(final Class<E> elementType) {
        this.elementType = elementType;
        try {
          this.universe = (E[]) elementType.getMethod("values").invoke(null);
        } catch (final ReflectiveOperationException e) {
          throw new IllegalArgumentException("Not an enum type: " + elementType, e);
        }
        if (this.universe.length > 64) {
          throw new IllegalArgumentException("More than 64 enum elements are not allowed");
        }
      }
    
      // Copy everything else from java.util.RegularEnumSet
      // ...
    }
    

    这个类现在是我所有枚举集的基础:

    @Embeddable
    public class InterestsSet extends PersistentEnumSet<InterestsEnum> {
      public InterestsSet() {
        super(InterestsEnum.class);
      }
    }
    

    我可以在我的实体中使用该集合:

    @Entity
    public class MyEntity {
      // ...
      @Embedded
      @AttributeOverride(name="elements", column=@Column(name="interests"))
      private InterestsSet interests = new InterestsSet();
    }
    

    优点:

    • 在代码中使用类型安全且高性能的枚举集(有关说明,请参见 java.util.EnumSet
    • 该集合只是数据库中的一个数字列
    • 一切都是纯 JPA(没有提供者特定的自定义类型
    • 与其他解决方案相比,相同类型的新字段的简单(和简短)声明

    缺点:

    • 代码重复(RegularEnumSetPersistentEnumSet 几乎相同)
      • 您可以将EnumSet.noneOf(enumType) 的结果包装在您的PersistenEnumSet 中,声明AccessType.PROPERTY 并提供两种使用反射来读取和写入elements 字段的访问方法
    • 应存储在持久集中的每个枚举类都需要一个附加集类
      • 如果您的持久性提供程序支持没有公共构造函数的嵌入,您可以将 @Embeddable 添加到 PersistentEnumSet 并删除 额外课程 (... interests = new PersistentEnumSet&lt;&gt;(InterestsEnum.class);)
    • 如果您的实体中有多个PersistentEnumSet,则必须使用@AttributeOverride,如我的示例中所示(否则两者都将存储到同一列“元素”中)
    • 在构造函数中使用反射访问values() 不是最佳的(尤其是在查看性能时),但其他两个选项也有其缺点:
      • EnumSet.getUniverse() 这样的实现使用了sun.misc
      • 提供值数组作为参数存在给定值不正确的风险
    • 只支持最多 64 个值的枚举(这真的是个缺点吗?)
      • 您可以改用 BigInteger
    • 在条件查询或 JPQL 中使用元素字段并不容易
      • 如果您的数据库支持,您可以使用二元运算符或具有适当功能的位掩码列

    【讨论】:

      【解决方案6】:

      JPA 中的集合指的是一对多或多对多的关系,它们只能包含其他实体。抱歉,您需要将这些枚举包装在一个实体中。如果您考虑一下,无论如何您都需要某种 ID 字段和外键来存储此信息。除非你做一些疯狂的事情,比如将逗号分隔的列表存储在字符串中(不要这样做!)。

      【讨论】:

      • 这仅对 JPA 1.0 有效。在 JPA 2.0 中,您可以使用 @ElementCollection 注释,如上所示。
      猜你喜欢
      • 1970-01-01
      • 2016-12-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-02-10
      • 1970-01-01
      • 2012-09-04
      相关资源
      最近更新 更多