एनपीए नक्शा Enums का संग्रह


91

क्या JPA में एंटिटी क्लास के भीतर Enums के संग्रह को मैप करने का एक तरीका है? या एकमात्र समाधान Enum को किसी अन्य डोमेन वर्ग के साथ लपेटना और इसका उपयोग संग्रह को मैप करने के लिए करना है?

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

मैं हाइबरनेट JPA कार्यान्वयन का उपयोग कर रहा हूं, लेकिन निश्चित रूप से कार्यान्वयन अज्ञेय समाधान पसंद करेंगे।

जवाबों:


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
चूंकि आपने हाइबरनेट का उल्लेख किया है, तो मुझे लगा कि यह उत्तर विक्रेता-विशिष्ट हो सकता है, लेकिन मुझे नहीं लगता कि यह तब तक है जब तक कि JoinTable अन्य कार्यान्वयन में परेशानी का कारण नहीं बनता है। मैंने जो देखा है, उससे मेरा मानना ​​है कि इसके बजाय CollectionTable का उपयोग किया जाना चाहिए। यही मैंने अपने उत्तर में इस्तेमाल किया और यह मेरे लिए काम करता है (हालाँकि हाँ, मैं अभी हाइबरनेट का भी उपयोग कर रहा हूँ।)
Spaaarky21

मुझे पता है कि यह एक पुराना धागा है, लेकिन हम javax.persistence का उपयोग करके उसी तरह की चीज़ को लागू कर रहे हैं। जब हम जोड़ते हैं: @ElementCollection (targetClass = Roles.class) @CollectionTable (नाम = "USER_ROLES", joinColumns = @ JoinColumn (नाम = "USER_ID")) @ कॉलम (नाम = "ROLE", nullable = false) EnumType.STRING) निजी सेट <भूमिकाएँ> भूमिकाएँ; हमारे उपयोगकर्ता तालिका में, पूरे मॉडल पैकेज में चीजें बाहर निकलती हैं। हमारे उपयोगकर्ता ऑब्जेक्ट में, यहां तक ​​कि प्राथमिक कुंजी के @Id ... @ जेनरेटवैल्यू जनरेटर और पहले @OneToMany बिल्ड पर नासमझ त्रुटियों को फेंक देते हैं।
LinuxLars

इसके लिए जो मूल्य है - जो त्रुटियां मैं देख रहा हूं वे बग हैं - मुद्दों। Jboss.org/browse/JBIDE-16016
LinuxLars

65

एंडी के जवाब में लिंक 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
आपको वास्तव में इससे बाहर की जरूरत है @ElementCollectionऔर Collection<InterestsEnum> interests; बाकी संभावित रूप से उपयोगी लेकिन अनावश्यक है। उदाहरण के लिए @Enumerated(EnumType.STRING)आपके डेटाबेस में मानव पठनीय तार लगाता है।
कोरेथन

2
आप सही हैं - इस उदाहरण में, आप पर भरोसा करते हैं सकता है @Columnके nameनिहित जा रहा है। मैं सिर्फ यह स्पष्ट करना चाहता था कि जब @ कॉलम पहले छोड़ दिया गया है तो क्या निहित है। और @Enumerated को हमेशा अनुशंसित किया जाता है क्योंकि ऑर्डिनल डिफ़ॉल्ट के लिए एक भयानक चीज है। :)
स्पाकार्की 21

मुझे लगता है कि इसके उल्लेख के लायक है कि आपको वास्तव में person_interest table
lukaszrys

मुझे इसे काम करने के लिए joinColumn पैरामीटर को जोड़ना था@CollectionTable(name="person_interest", joinColumns = {@JoinColumn(name="person_id")})
Tiago

7

मैं इसे इस सरल तरीके से पूरा करने में सक्षम था:

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

यहां बताए अनुसार आलसी लोडिंग inizializing त्रुटि से बचने के लिए उत्सुक लोडिंग की आवश्यकता होती है


5

मैं एक निरंतर संशोधन करने के लिए java.util.RegularEnumSet के एक मामूली संशोधन का उपयोग कर रहा हूं:

@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विवरण के लिए देखें )
  • सेट डेटाबेस में सिर्फ एक संख्यात्मक कॉलम है
  • सब कुछ सादा जेपीए है (कोई प्रदाता विशिष्ट कस्टम प्रकार नहीं )
  • अन्य समाधानों की तुलना में एक ही प्रकार के नए क्षेत्रों की आसान (और छोटी) घोषणा

कमियां:

  • कोड दोहराव (RegularEnumSet और PersistentEnumSetलगभग समान हैं)
    • आप EnumSet.noneOf(enumType)अपने परिणाम को रैप कर सकते हैं PersistenEnumSet, घोषित कर सकते हैं AccessType.PROPERTYऔर दो एक्सेस तरीके प्रदान कर सकते हैं जो पढ़ने और लिखने के लिए प्रतिबिंब का उपयोग करते हैंelements फील्ड हैं
  • प्रत्येक enum वर्ग के लिए एक अतिरिक्त सेट क्लास की आवश्यकता होती है जिसे एक स्थिर सेट में संग्रहीत किया जाना चाहिए
    • यदि आपका दृढ़ता प्रदाता सार्वजनिक निर्माणकर्ता के बिना एम्बेड का समर्थन करता है, तो आप अतिरिक्त वर्ग @Embeddableको जोड़ सकते हैं PersistentEnumSetऔर छोड़ सकते हैं ( ... interests = new PersistentEnumSet<>(InterestsEnum.class);)
  • आपको एक का उपयोग करना चाहिए @AttributeOverride मेरे उदाहरण में दिए गए , यदि आपको PersistentEnumSetअपनी इकाई में एक से अधिक मिला है (अन्यथा दोनों को एक ही कॉलम "तत्वों" में संग्रहीत किया जाएगा)
  • की पहुँच values()कंस्ट्रक्टर में प्रतिबिंब साथ इष्टतम नहीं है (विशेषकर जब प्रदर्शन को देखते हुए), लेकिन दो अन्य विकल्पों में उनकी कमियां भी हैं:
    • एक कार्यान्वयन की तरह एक का EnumSet.getUniverse()उपयोग करता हैsun.misc वर्ग
    • मान सरणी को पैरामीटर के रूप में प्रदान करना जोखिम है कि दिए गए मान सही नहीं हैं
  • केवल 64 मान वाले एनम का समर्थन किया जाता है (क्या यह वास्तव में एक खामी है?)
    • आप इसके बजाय BigInteger का उपयोग कर सकते हैं
  • मापदंड क्वेरी या JPQL में तत्वों के क्षेत्र का उपयोग करना आसान नहीं है
    • यदि आप अपने डेटाबेस का समर्थन करते हैं, तो आप उपयुक्त कार्यों के साथ बाइनरी ऑपरेटरों या एक बिटमास्क कॉलम का उपयोग कर सकते हैं

4

tl; dr एक लघु समाधान निम्नलिखित होगा:

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

लंबा जवाब यह है कि इस एनोटेशन के साथ जेपीए एक तालिका बनाएगा जो मुख्य वर्ग के पहचानकर्ता (इस मामले में पर्सनक्लास) की ओर इशारा करते हुए इंटरस्टीनम की सूची बनाएगी।

@ElementCollections निर्दिष्ट करता है कि JPA Enum के बारे में जानकारी कहां प्राप्त कर सकता है

@CollectionTable व्यक्ति से InterestsEnum तक संबंध रखने वाली तालिका बनाएँ

@Enumerated (EnumType.STRING) JPA को बताएं कि Enum को स्ट्रिंग के रूप में बनाए रखने के लिए, EnumType.ORDINAL हो सकता है


इस मामले में मैं इस संग्रह को बदल नहीं सकता क्योंकि यह एक PersistenceSet के रूप में बचाता है जो अपरिवर्तनीय है।
निकोलजॉज92

मेरी गलती। हम इस सेट को एक सेटर के साथ बदल सकते हैं।
निकोलजॉब २०

0

जेपीए में संग्रह एक-से-कई या कई-से-कई रिश्तों का उल्लेख करते हैं और वे केवल अन्य संस्थाओं को शामिल कर सकते हैं। क्षमा करें, लेकिन आपको उन शत्रुओं को एक इकाई में लपेटने की आवश्यकता होगी। यदि आप इसके बारे में सोचते हैं, तो आपको इस जानकारी को संग्रहीत करने के लिए किसी प्रकार की आईडी फ़ील्ड और विदेशी कुंजी की आवश्यकता होगी। जब तक कि आप कुछ पागल नहीं करते हैं जैसे कि एक स्ट्रिंग में अल्पविराम से अलग की गई सूची को संग्रहीत करें (ऐसा न करें!)।


8
यह केवल JPA 1.0 के लिए मान्य है। JPA 2.0 में आप ऊपर दिखाए गए अनुसार @ElementCollection एनोटेशन का उपयोग कर सकते हैं।
रस्टीक्स
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.