JPA地图集合枚举
在JPA中有一种方法可以映射Entity类中的枚举集合吗?或者唯一的解决办法是将Enum与另一个域类打包并使用它来映射集合?JPA地图集合枚举
@Entity
public class Person {
public enum InterestsEnum {Books, Sport, etc... }
//@???
Collection<InterestsEnum> interests;
}
我正在使用Hibernate JPA实现,但当然会更喜欢实现不可知的解决方案。
使用Hibernate,你可以做
@CollectionOfElements(targetElement = InterestsEnum.class)
@JoinTable(name = "tblInterests", joinColumns = @JoinColumn(name = "personID"))
@Column(name = "interest", nullable = false)
@Enumerated(EnumType.STRING)
Collection<InterestsEnum> interests;
JPA中的集合是指一对多或多对多关系,它们只能包含其他实体。对不起,您需要将这些枚举包装在一个实体中。如果你仔细想想,无论如何你都需要某种ID字段和外键来存储这些信息。这是除非你做一些疯狂的事情,比如在一个字符串中存储逗号分隔列表(不要这样做!)。
这仅对JPA 1.0有效。在JPA 2.0中,您可以使用@ElementCollection注释,如上所示。 – rustyx 2010-12-27 12:17:21
安迪的回答的联系是在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", [email protected](name="interests"))
private InterestsSet interests = new InterestsSet();
}
优点:
- 在你的代码类型安全和高性能的枚举集工作(参见
java.util.EnumSet
的描述) - 集是在数据库中只有一个数字列
- 一切都是原生的JPA(没有提供具体自定义类型)
- 容易(和短)的相同类型的新的字段声明,与其他解决方案
缺点相比:
- 代码重复(
RegularEnumSet
和PersistentEnumSet
几乎相同)- 你可以换的
EnumSet.noneOf(enumType)
结果在PersistenEnumSet
,声明AccessType.PROPERTY
并提供使用反射两种接入方式来读取和写入elements
场
- 你可以换的
- 对于应存储在持久集中的每个枚举类,需要额外的集合类
- 如果您的持久性提供者支持TS embeddables没有公共构造函数,你可以添加到
@Embeddable
和PersistentEnumSet
降 额外的类(... interests = new PersistentEnumSet<>(InterestsEnum.class);
)
- 如果您的持久性提供者支持TS embeddables没有公共构造函数,你可以添加到
- 您必须使用
@AttributeOverride
,因为在我的例子给出的,如果你有一个以上的PersistentEnumSet
在你的实体(否则两者都将被存储到同一列“元素”) -
values()
与反射在构造函数中的访问不是最优的(尤其是在查看性能时),但其他两个选项也有它们的缺点:- 一个工具像
EnumSet.getUniverse()
通货膨胀利用的sun.misc
类 - 提供了值数组作为参数具有风险,即给定值是不正确的参数
- 一个工具像
- 只有拥有多达64个值枚举的支持(是真的退税?)
- 你可以使用的BigInteger代替
- 这是不容易的使用条件查询元素域或JPQL
- 如果你可以用二元运算符或相应的功能位掩码列,您数据库支持
我能够在这个简单的方式来实现这一目标:
@ElementCollection(fetch = FetchType.EAGER)
Collection<InterestsEnum> interests;
为了避免延迟加载inizializing错误,需要进行急切加载,如解释here所述。
如果有人现在阅读此文件... @CollectionOfElements现在已被弃用,请改为使用:@ElementCollection – 2010-04-21 16:59:42
您可以在此问题的答案中找到示例:http://stackoverflow.com/q/3152787/363573 – Stephan 2011-08-13 22:24:29
既然你上面提到的Hibernate,我认为这个答案可能是特定于供应商的,但我不认为它是这样的,除非在其他实现中使用JoinTable会导致麻烦。从我所看到的,我相信应该使用CollectionTable。这就是我在我的答案中使用的,它适用于我(尽管是的,我现在也在使用Hibernate)。 – spaaarky21 2012-03-05 20:51:59