枚举的JPA地图集合


91

JPA中是否有一种方法可以在Entity类中映射枚举的集合?还是唯一的解决方案是用另一个域类包装Enum并使用它来映射集合?

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

我正在使用Hibernate JPA实现,但是当然更喜欢与实现无关的解决方案。

Answers:


112

使用休眠可以做到

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

141
如果有人现在阅读此内容……@CollectionOfElements现在已被弃用,请使用:@ElementCollection

2
您可以在此问题的答案中找到示例:stackoverflow.com/q/3152787/363573
Stephan

1
自从您提到Hibernate以来,我认为这个答案可能是特定于供应商的,但是除非使用JoinTable在其他实现中引起麻烦,否则我认为不是。从我所看到的,我相信应该改用CollectionTable。这就是我在答案中使用的内容,它对我有用(尽管是的,我现在也在使用Hibernate。)
spaaarky21 2012年

我知道这是一个旧线程,但是我们正在使用javax.persistence实现相同的事情。当我们添加时:@ElementCollection(targetClass = Roles.class)@CollectionTable(name =“ USER_ROLES”,joinColumns = @ JoinColumn(name =“ USER_ID”))@Column(name =“ ROLE”,nullable = false)@Enumerated( EnumType.STRING)私有Set <Roles>角色;在我们的User表中,事情在整个模型包中浮出水面。在我们的User对象中,甚至主键的@Id ... @ GeneratedValue生成器上的错误以及第一个@OneToMany在构建时都会抛出愚蠢的错误。
LinuxLars

对于它的价值-我看到的错误是一个错误-issues.jboss.org/browse/JBIDE-16016
LinuxLars

65

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

4
遗憾的是,一些“管理员”决定删除该答案而没有给出理由(关于此处课程的标准)。作为参考,它是datanucleus.org/products/accessplatform_3_0/jpa/orm/…–
DataNucleus

2
您实际需要的就是@ElementCollectionCollection<InterestsEnum> interests; 其余的可能有用,但不必要。例如,@Enumerated(EnumType.STRING)将人类可读的字符串放入数据库中。
CorayThan

2
你是正确的-在这个例子中,你可以依靠@Columnname被暗示。我只是想澄清省略@Column时的隐含含义。始终建议使用@Enumerated,因为默认情况下序数是一件很糟糕的事情。:)
spaaarky21

我认为值得一提的是,您实际上需要person_interest表
lukaszrys 2014年

我必须添加joinColumn参数以使其起作用@CollectionTable(name="person_interest", joinColumns = {@JoinColumn(name="person_id")})
Tiago

7

我能够通过以下简单方式完成此任务:

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

预先加载需要,以避免延迟加载inizializing错误的解释在这里


5

我使用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(没有提供程序特定的自定义类型
  • 与其他解决方案相比,轻松(简短)声明相同类型的新字段

缺点:

  • 重复代码(RegularEnumSetPersistentEnumSet几乎相同)
    • 您可以将的结果包装在EnumSet.noneOf(enumType)PersistenEnumSet,声明AccessType.PROPERTY并提供两个访问方法,这些方法使用反射来读取和写入elements字段
  • 每个枚举类都需要一个附加的集合类,该类应存储在一个持久性集合中
    • 如果你的持久提供商支持embeddables没有公共构造函数,你可以添加@EmbeddablePersistentEnumSet并删除多余的类(... interests = new PersistentEnumSet<>(InterestsEnum.class);
  • @AttributeOverride如果您有多个,则必须使用我的示例中给出的,PersistentEnumSet的实体中实体否则,这两个实体都将存储在同一列“元素”中)
  • 的访问 values()在构造函数使用with反射不是最佳的(特别是在查看性能时),但是其他两个选项也有其缺点:
    • 像这样的EnumSet.getUniverse()实现利用sun.misc
    • 将values数组作为参数提供时,存在给定值不正确的风险
  • 仅支持最多64个值的枚举(这真的有缺点吗?)
    • 您可以改用BigInteger
  • 在条件查询或JPQL中使用elements字段并不容易
    • 如果数据库支持,则可以使用具有适当功能的二进制运算符或位掩码列

4

tl; dr一个简短的解决方案是:

@ElementCollection(targetClass = InterestsEnum.class)
@CollectionTable
@Enumerated(EnumType.STRING)
Collection<InterestsEnum> interests;

长答案是,使用此批注,JPA将创建一个表,该表将保存指向主类标识符(在本例中为Person.class)的InterestsEnum列表。

@ElementCollections指定JPA在哪里可以找到有关枚举的信息

@CollectionTable创建一个表,该表保存从Person到InterestsEnum的关系

@Enumerated(EnumType.STRING)告诉JPA将Enum保留为String,可以是EnumType.ORDINAL


在这种情况下,我无法更改此集合,因为它另存为不可变的PersistenceSet。
Nicolazz92

我的错。我们可以使用setter来更改此设置。
Nicolazz92

0

JPA中的集合是指一对多或多对多关系,并且它们只能包含其他实体。抱歉,但是您需要将这些枚举包装在实体中。如果考虑一下,无论如何,您都需要某种ID字段和外键来存储此信息。除非您做一些疯狂的事情,例如将逗号分隔的列表存储在String中(不要这样做!)。


8
这仅对JPA 1.0有效。在JPA 2.0中,可以使用@ElementCollection批注,如上所示。
rustyx 2010年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.