2009-01-06 104 views
64

在JPA中有一种方法可以映射Entity类中的枚举集合吗?或者唯一的解决办法是将Enum与另一个域类打包并使用它来映射集合?JPA地图集合枚举

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

我正在使用Hibernate JPA实现,但当然会更喜欢实现不可知的解决方案。

回答

86

使用Hibernate,你可以做

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

如果有人现在阅读此文件... @CollectionOfElements现在已被弃用,请改为使用:@ElementCollection – 2010-04-21 16:59:42

+2

您可以在此问题的答案中找到示例:http://stackoverflow.com/q/3152787/363573 – Stephan 2011-08-13 22:24:29

+0

既然你上面提到的Hibernate,我认为这个答案可能是特定于供应商的,但我不认为它是这样的,除非在其他实现中使用JoinTable会导致麻烦。从我所看到的,我相信应该使用CollectionTable。这就是我在我的答案中使用的,它适用于我(尽管是的,我现在也在使用Hibernate)。 – spaaarky21 2012-03-05 20:51:59

0

JPA中的集合是指一对多或多对多关系,它们只能包含其他实体。对不起,您需要将这些枚举包装在一个实体中。如果你仔细想想,无论如何你都需要某种ID字段和外键来存储这些信息。这是除非你做一些疯狂的事情,比如在一个字符串中存储逗号分隔列表(不要这样做!)。

+7

这仅对JPA 1.0有效。在JPA 2.0中,您可以使用@ElementCollection注释,如上所示。 – rustyx 2010-12-27 12:17:21

45

安迪的回答的联系是在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; 
} 
3

我对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(没有提供具体自定义类型
  • 容易(和短)的相同类型的新的字段声明,与其他解决方案

缺点相比:

  • 代码重复(RegularEnumSetPersistentEnumSet几乎相同)
    • 你可以换的EnumSet.noneOf(enumType)结果在PersistenEnumSet,声明AccessType.PROPERTY并提供使用反射两种接入方式来读取和写入elements
  • 对于应存储在持久集中的每个枚举类,需要额外的集合类
    • 如果您的持久性提供者支持TS embeddables没有公共构造函数,你可以添加到@EmbeddablePersistentEnumSet降 额外的类(... interests = new PersistentEnumSet<>(InterestsEnum.class);
  • 您必须使用@AttributeOverride,因为在我的例子给出的,如果你有一个以上的PersistentEnumSet在你的实体(否则两者都将被存储到同一列“元素”)
  • values()与反射在构造函数中的访问不是最优的(尤其是在查看性能时),但其他两个选项也有它们的缺点:
    • 一个工具像EnumSet.getUniverse()通货膨胀利用的sun.misc
    • 提供了值数组作为参数具有风险,即给定值是不正确的参数
  • 只有拥有多达64个值枚举的支持(是真的退税?)
    • 你可以使用的BigInteger代替
  • 这是不容易的使用条件查询元素域或JPQL
    • 如果你可以用二元运算符或相应的功能位掩码列,您数据库支持
1

我能够在这个简单的方式来实现这一目标:

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

为了避免延迟加载inizializing错误,需要进行急切加载,如解释here所述。