JPA地图收集的枚举

在JPA中有没有办法在Entity类中映射一个Enums集合? 或者唯一的解决办法是将Enum与另一个域类打包并用它来映射集合?

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

我正在使用Hibernate JPA实现,但当然会更喜欢实现不可知的解决scheme。

使用hibernate你可以做

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

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

我正在使用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(); } 

优点:

  • 在你的代码中设置一个types安全和高性能的枚举(参见java.util.EnumSet的描述)
  • 该集只是数据库中的一个数字列
  • 一切都是纯粹的JPA(没有提供者特定的自定义types
  • 与其他解决scheme相比,简单(和简短)的同types新领域的申报

缺点:

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

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

我能够以这种简单的方式完成这个工作:

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

如此处所述,为了避免延迟加载inizializing错误,需要进行急切的加载。