2009-01-06 105 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所述。