在JPA中有一种方法可以在Entity类中映射枚举的集合吗?或者唯一的解决方案是使用另一个域类包装Enum并使用它来映射集合?
@Entity public class Person { public enum InterestsEnum {Books, Sport, etc... } //@??? Collectioninterests; }
我正在使用Hibernate JPA实现,但当然更喜欢实现不可知的解决方案.
使用Hibernate你可以做到
@CollectionOfElements(targetElement = InterestsEnum.class) @JoinTable(name = "tblInterests", joinColumns = @JoinColumn(name = "personID")) @Column(name = "interest", nullable = false) @Enumerated(EnumType.STRING) Collectioninterests;
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 Collectioninterests; }
我能够通过以下简单方式完成此任务:
@ElementCollection(fetch = FetchType.EAGER) Collectioninterests;
预先加载需要,以避免延迟加载inizializing错误的解释在这里。
我使用java.util.RegularEnumSet的略微修改以具有持久的EnumSet:
@MappedSuperclass @Access(AccessType.FIELD) public class PersistentEnumSet> extends AbstractSet { private long elements; @Transient private final Class elementType; @Transient private final E[] universe; public PersistentEnumSet(final Class 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{ 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(没有提供程序特定的自定义类型)
与其他解决方案相比,轻松(简短)声明相同类型的新字段
缺点:
重复代码(RegularEnumSet
和PersistentEnumSet
几乎相同)
您可以将的结果包装在EnumSet.noneOf(enumType)
中PersistenEnumSet
,声明AccessType.PROPERTY
并提供两种访问方法,这些方法使用反射来读取和写入elements
字段
每个枚举类都需要一个附加的集合类,该类应存储在一个持久性集合中
如果你的持久提供商支持embeddables没有公共构造函数,你可以添加@Embeddable
到PersistentEnumSet
并删除多余的类(... interests = new PersistentEnumSet<>(InterestsEnum.class);
)
@AttributeOverride
如果您PersistentEnumSet
的实体中有多个实体,则必须使用如我的示例中给出的,否则两者都将存储在同一列“元素”中)
values()
在构造函数中使用with反射的访问不是最佳的(特别是在查看性能时),但是其他两个选项也有其缺点:
像这样的EnumSet.getUniverse()
实现利用了一个sun.misc
类
将values数组作为参数提供时,存在给定值不正确的风险
仅支持最多64个值的枚举(这真的有缺点吗?)
您可以改用BigInteger
在条件查询或JPQL中使用elements字段并不容易
如果数据库支持,则可以使用具有适当功能的二进制运算符或位掩码列