题
JPA 有没有办法在实体类中映射枚举集合?或者唯一的解决方案是将 Enum 与另一个域类包装并使用它来映射集合?
@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;
其他提示
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;
}
我能够以这种简单的方式实现这一目标:
@ElementCollection(fetch = FetchType.EAGER)
Collection<InterestsEnum> interests;
需要急切加载,以避免延迟加载inizializing错误,如这里。
我使用 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(没有特定于提供商 自定义类型)
- 与其他解决方案相比,可以轻松(且简短)地声明相同类型的新字段
缺点:
- 代码重复(
RegularEnumSet
和PersistentEnumSet
几乎相同)- 你可以将结果包装起来
EnumSet.noneOf(enumType)
在你的PersistenEnumSet
, , 宣布AccessType.PROPERTY
并提供两种使用反射来读写的访问方法elements
场地
- 你可以将结果包装起来
- 每个应存储在持久集中的枚举类都需要一个附加的集类
- 如果您的持久性提供程序支持没有公共构造函数的嵌入,您可以添加
@Embeddable
到PersistentEnumSet
并放下额外的班级(... interests = new PersistentEnumSet<>(InterestsEnum.class);
)
- 如果您的持久性提供程序支持没有公共构造函数的嵌入,您可以添加
- 您必须使用
@AttributeOverride
, ,如我的示例所示,如果您有多个PersistentEnumSet
在您的实体中(否则两者将存储到同一列“元素”中) - 的访问
values()
在构造函数中使用反射并不是最佳选择(特别是在考虑性能时),但其他两个选项也有其缺点:- 像这样的实现
EnumSet.getUniverse()
利用一个sun.misc
班级 - 提供值数组作为参数存在给定值不正确的风险
- 像这样的实现
- 仅支持最多 64 个值的枚举(这真的是一个缺点吗?)
- 您可以使用 BigInteger 代替
- 在条件查询或 JPQL 中使用 elements 字段并不容易
- 如果您的数据库支持,您可以使用二元运算符或具有适当函数的位掩码列
JPA中的集合是指一对多或多对多关系,它们只能包含其他实体。抱歉,您需要将这些枚举包装在实体中。如果你考虑一下,你需要某种ID字段和外键来存储这些信息。这是除非你做一些疯狂的事情,比如在String中存储以逗号分隔的列表(不要这样做!)。
不隶属于 StackOverflow