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新领域的申报
缺点:
- 代码重复(
RegularEnumSet
和PersistentEnumSet
几乎相同)- 您可以将
EnumSet.noneOf(enumType)
的结果包装在PersistenEnumSet
,声明AccessType.PROPERTY
并提供两种使用reflection读取和写入elements
字段的访问方法
- 您可以将
- 每个应该存储在持久集中的枚举类都需要额外的集合类
- 如果您的持久性提供程序支持不带公共构造函数的可embedded对象,则可以将
@Embeddable
添加到PersistentEnumSet
并删除额外的类(... interests = new PersistentEnumSet<>(InterestsEnum.class);
)
- 如果您的持久性提供程序支持不带公共构造函数的可embedded对象,则可以将
- 如果在实体中有多个
PersistentEnumSet
,则必须使用@AttributeOverride
(如我的示例中所示),否则两者都将存储到同一列“元素”中 - 在构造函数中使用reflection访问
values()
并不是最优的(特别是在查看性能时),但是其他两个选项也有其缺点:- 像
EnumSet.getUniverse()
实现使用了sun.misc
类 - 提供values数组作为参数具有给定值不正确的风险
- 像
- 只支持多达64个值的枚举(这真的是一个缺点?)
- 您可以改用BigInteger
- 在标准查询或JPQL中使用元素字段并不容易
- 如果数据库支持,可以使用二进制运算符或位掩码列
JPA中的集合是指一对多或多对多关系,它们只能包含其他实体。 对不起,但你需要将这些枚举包装在一个实体中。 如果你仔细想想,无论如何你都需要一些ID字段和外键来存储这些信息。 这是除非你做一些疯狂的事情,比如在一个string中存储一个以逗号分隔的列表(不要这样做!)。
我能够以这种简单的方式完成这个工作:
@ElementCollection(fetch = FetchType.EAGER) Collection<InterestsEnum> interests;
如此处所述,为了避免延迟加载inizializing错误,需要进行急切的加载。