JPA में टाइप लिस्ट <String> की संपत्ति कैसे बनी रहे?


158

टाइप के क्षेत्र के साथ एक इकाई पाने के लिए सबसे स्मार्ट तरीका क्या है?

Command.java

package persistlistofstring;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import javax.persistence.Basic;
import javax.persistence.Entity;
import javax.persistence.EntityManager;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Persistence;

@Entity
public class Command implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    Long id;
    @Basic
    List<String> arguments = new ArrayList<String>();

    public static void main(String[] args) {
        Command command = new Command();

        EntityManager em = Persistence
                .createEntityManagerFactory("pu")
                .createEntityManager();
        em.getTransaction().begin();
        em.persist(command);
        em.getTransaction().commit();
        em.close();

        System.out.println("Persisted with id=" + command.id);
    }
}

इस कोड का उत्पादन:

> Exception in thread "main" javax.persistence.PersistenceException: No Persistence provider for EntityManager named pu: Provider named oracle.toplink.essentials.PersistenceProvider threw unexpected exception at create EntityManagerFactory: 
> oracle.toplink.essentials.exceptions.PersistenceUnitLoadingException
> Local Exception Stack: 
> Exception [TOPLINK-30005] (Oracle TopLink Essentials - 2.0.1 (Build b09d-fcs (12/06/2007))): oracle.toplink.essentials.exceptions.PersistenceUnitLoadingException
> Exception Description: An exception was thrown while searching for persistence archives with ClassLoader: sun.misc.Launcher$AppClassLoader@11b86e7
> Internal Exception: javax.persistence.PersistenceException: Exception [TOPLINK-28018] (Oracle TopLink Essentials - 2.0.1 (Build b09d-fcs (12/06/2007))): oracle.toplink.essentials.exceptions.EntityManagerSetupException
> Exception Description: predeploy for PersistenceUnit [pu] failed.
> Internal Exception: Exception [TOPLINK-7155] (Oracle TopLink Essentials - 2.0.1 (Build b09d-fcs (12/06/2007))): oracle.toplink.essentials.exceptions.ValidationException
> Exception Description: The type [interface java.util.List] for the attribute [arguments] on the entity class [class persistlistofstring.Command] is not a valid type for a serialized mapping. The attribute type must implement the Serializable interface.
>         at oracle.toplink.essentials.exceptions.PersistenceUnitLoadingException.exceptionSearchingForPersistenceResources(PersistenceUnitLoadingException.java:143)
>         at oracle.toplink.essentials.ejb.cmp3.EntityManagerFactoryProvider.createEntityManagerFactory(EntityManagerFactoryProvider.java:169)
>         at javax.persistence.Persistence.createEntityManagerFactory(Persistence.java:110)
>         at javax.persistence.Persistence.createEntityManagerFactory(Persistence.java:83)
>         at persistlistofstring.Command.main(Command.java:30)
> Caused by: 
> ...

जवाबों:


197

कुछ JPA 2 कार्यान्वयन का उपयोग करें: यह एक @ElementCollection एनोटेशन जोड़ता है, जो कि हाइबरनेट एक के समान है, जो वास्तव में आपकी आवश्यकता है। यहाँ एक उदाहरण है

संपादित करें

जैसा कि नीचे टिप्पणी में कहा गया है, सही जेपीए 2 कार्यान्वयन है

javax.persistence.ElementCollection

@ElementCollection
Map<Key, Value> collection;

देखें: http://docs.oracle.com/javaee/6/api/javax/persistence/ElementColiveion.html


1
मेरी गलती थी कि @ OneToMany एनोटेशन को जोड़ने के लिए ... इसे हटाने के बाद और @ ElementCollection छोड़ने के बाद इसने काम किया
विली मेंजेल

47

एक पुराने धागे को पुनर्जीवित करने के लिए क्षमा करें, लेकिन क्या किसी को वैकल्पिक समाधान की तलाश में होना चाहिए जहां आप अपनी स्ट्रिंग सूचियों को अपने डेटाबेस में एक क्षेत्र के रूप में संग्रहीत करते हैं, यहां बताया गया है कि मैंने इसे कैसे हल किया। इस तरह एक कनवर्टर बनाएँ:

import java.util.Arrays;
import java.util.List;

import javax.persistence.AttributeConverter;
import javax.persistence.Converter;

@Converter
public class StringListConverter implements AttributeConverter<List<String>, String> {
    private static final String SPLIT_CHAR = ";";

    @Override
    public String convertToDatabaseColumn(List<String> stringList) {
        return String.join(SPLIT_CHAR, stringList);
    }

    @Override
    public List<String> convertToEntityAttribute(String string) {
        return Arrays.asList(string.split(SPLIT_CHAR));
    }
}

अब इसे इस तरह अपने Entities पर उपयोग करें:

@Convert(converter = StringListConverter.class)
private List<String> yourList;

डेटाबेस में आपकी सूची को foo; बार के रूप में संग्रहीत किया जाएगा; फ़ॉबोरर और आपके जावा ऑब्जेक्ट में आपको उन स्ट्रिंग्स के साथ एक सूची मिलेगी।

आशा है कि यह किसी के लिए उपयोगी है।


क्या यह उस क्षेत्र की सामग्री द्वारा परिणामों को फ़िल्टर करने के लिए जेपीए रिपॉजिटरी के साथ काम करेगा?
कृपया_डॉट_बुल्ली_मे_ओस___________________06_05'५:41

1
@Please_Dont_Bully_Me_SO_Lords यह उस उपयोग के मामले के लिए कम उपयुक्त है क्योंकि आपका डेटा डेटाबेस में "foo; बार; फोब्बर" के रूप में होगा। यदि आप डेटा के लिए क्वेरी करना चाहते हैं, तो संभवतः एक ElementCollection + JoinTable आपकी स्थिति के लिए जाने का तरीका है।
जोंक वैन डेर कोगेल

इसका मतलब यह भी है कि SPLIT_CHARआपके स्ट्रिंग में कोई घटना नहीं हो सकती है ।
क्रश करें

@ क्रश जो सही है। हालाँकि आप इसे सही ढंग से सीमांकित करने के बाद अपने स्ट्रिंग एन्कोडिंग उदाहरण के लिए इसके लिए अनुमति दे सकते हैं। लेकिन मैंने जो समाधान यहां पोस्ट किया है वह मुख्य रूप से सरल उपयोग के मामलों के लिए है; अधिक जटिल स्थितियों के लिए शायद आप एलीमेंटकॉलशन + जॉइनटेबल के साथ बेहतर किराया करेंगे
वैन डेर कोगेल

कृपया अपना कोड ठीक करें। मैं इसे 'लाइब्रेरी कोड' मानता हूं, इसलिए इसे रक्षात्मक होना चाहिए। कम से कम इसकी अशक्त जाँच होनी चाहिए
ZZ 5

30

यह उत्तर पूर्व JPA2 कार्यान्वयन के लिए बनाया गया था, यदि आप JPA2 का उपयोग कर रहे हैं, तो उपरोक्त ElementCollection उत्तर देखें:

एक मॉडल ऑब्जेक्ट के अंदर वस्तुओं की सूची को आम तौर पर किसी अन्य ऑब्जेक्ट के साथ "OneToMany" रिश्ते माना जाता है। हालाँकि, एक स्ट्रिंग एक-से-कई संबंधों का स्वीकार्य ग्राहक नहीं है (क्योंकि यह एक आईडी नहीं है।

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

वैकल्पिक रूप से, आप अपनी सूची को @ ट्रांसफॉर्मेंट में बदल सकते हैं और अपनी कक्षा में एक और फ़ील्ड (argStorage) जोड़ सकते हैं जो या तो VARCHAR () या एक CLOB है। फिर आपको 3 फ़ंक्शंस जोड़ने की आवश्यकता होगी: उनमें से 2 समान हैं और स्ट्रिंग्स की अपनी सूची को एक एकल स्ट्रिंग (argStorage में) में इस तरह परिवर्तित कर दिया जाना चाहिए कि आप आसानी से उन्हें अलग कर सकें। @PrePersist और @PreUpdate के साथ इन दो कार्यों (जो प्रत्येक एक ही काम करते हैं) की व्याख्या करें। अंत में, तीसरा फ़ंक्शन जोड़ें जो फिर से स्ट्रिंग्स की सूची में argStorage को विभाजित करता है और इसे @PostLoad एनोटेट करता है। जब भी आप कमांड को स्टोर करने के लिए जाते हैं, तो यह आपके CLOB को स्ट्रिंग्स के साथ अपडेट रखेगा, और डीजी को स्टोर करने से पहले आप argStorage क्षेत्र को अपडेट रखें।

मैं अभी भी पहला मामला करने का सुझाव देता हूं। बाद में वास्तविक संबंधों के लिए यह अच्छा अभ्यास है।


ArrayList से बदलना <String> String के साथ अल्पविराम से अलग किए गए मानों ने मेरे लिए काम किया।
क्रिस डेल

2
लेकिन यह आपको उस फ़ील्ड को क्वेरी करते समय स्टेटमेंट की तरह (imho) बदसूरत का उपयोग करने के लिए मजबूर करता है।
व्हिस्की सियार

हां, जैसा कि मैंने कहा ... पहला विकल्प करें, यह बेहतर है। यदि आप इसे करने के लिए खुद को नहीं ला सकते हैं, तो विकल्प 2 काम कर सकता है।
बिलजमदेव

15

हाइबरनेट के साथ जावा दृढ़ता के अनुसार

एनोटेशन के साथ मूल्य प्रकारों के मानचित्रण संग्रह [...]। लेखन के समय यह जावा पर्सिस्टेंस मानक का हिस्सा नहीं है

यदि आप हाइबरनेट का उपयोग कर रहे हैं, तो आप कुछ ऐसा कर सकते हैं:

@org.hibernate.annotations.CollectionOfElements(
    targetElement = java.lang.String.class
)
@JoinTable(
    name = "foo",
    joinColumns = @JoinColumn(name = "foo_id")
)
@org.hibernate.annotations.IndexColumn(
    name = "POSITION", base = 1
)
@Column(name = "baz", nullable = false)
private List<String> arguments = new ArrayList<String>();

अपडेट: ध्यान दें, यह अब JPA2 में उपलब्ध है।



9

JPA के हाइबरनेट कार्यान्वयन का उपयोग करते समय, मैंने पाया है कि सूची के बजाय केवल ArrayList के रूप में प्रकार की घोषणा करने से हाइबरनेट को डेटा की सूची संग्रहीत करने की अनुमति मिलती है।

स्पष्ट रूप से इकाई वस्तुओं की सूची बनाने की तुलना में इसके कई नुकसान हैं। कोई आलसी लोडिंग, अन्य वस्तुओं से सूची में संस्थाओं का संदर्भ लेने की क्षमता नहीं, शायद डेटाबेस प्रश्नों के निर्माण में अधिक कठिनाई। हालाँकि जब आप काफी आदिम प्रकारों की सूचियों के साथ काम कर रहे होते हैं जो आप हमेशा इकाई के साथ उत्सुकता से प्राप्त करना चाहेंगे, तो यह दृष्टिकोण मुझे ठीक लगता है।

@Entity
public class Command implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    Long id;

    ArrayList<String> arguments = new ArrayList<String>();


}

2
धन्यवाद। सभी जेपीए कार्यान्वयन के साथ यह कार्य, Arraylist सीरियल है एक BLOB क्षेत्र में सहेजा गया है। इस पद्धति की समस्याएं यह हैं कि 1) BLOB का आकार 2 निर्धारित है) आप सरणी तत्वों 3 को खोज सकते हैं या अनुक्रमणित कर सकते हैं) केवल जावा धारावाहिक प्रारूप के बारे में जागरूक ग्राहक ही इन तत्वों को पढ़ सकता है।
एंड्रिया फ्रांसिया

यदि आप इसके साथ इस दृष्टिकोण की कोशिश करते हैं तो आपको सर्वर स्टार्टअप पर अपवाद @OneToMany @ManyToOne @ElementCollectionमिलेगा Caused by: org.hibernate.AnnotationException: Illegal attempt to map a non collection as a @OneToMany, @ManyToMany or @CollectionOfElements। क्योंकि हाइबरनेट्स आपको संग्रह इंटरफेस का उपयोग करना चाहता है।
परमवीर सिंह करवाल

9

मुझे भी यही समस्या थी इसलिए मैंने दिए गए संभावित समाधान का निवेश किया लेकिन अंत में मैंने अपना 'लागू करने का फैसला किया;' स्ट्रिंग की अलग सूची।

तो मेरे पास है

// a ; separated list of arguments
String arguments;

public List<String> getArguments() {
    return Arrays.asList(arguments.split(";"));
}

इस तरह से सूची डेटाबेस तालिका में आसानी से पढ़ने योग्य / संपादन योग्य है;


1
यह पूरी तरह से मान्य है लेकिन अपने आवेदन की वृद्धि और स्कीमा विकास पर विचार करें। शायद ही कभी (निकट) भविष्य में आप अंततः इकाई आधारित दृष्टिकोण पर जा सकते हैं।
व्हिस्की सिएरा

मैं सहमत हूं, यह पूरी तरह से मान्य है। हालांकि, मैं कोड के कार्यान्वयन के साथ-साथ तर्क की पूरी तरह से समीक्षा करने का सुझाव देता हूं। यदि स्ट्रिंग argumentsएक्सेस अनुमतियों की सूची है, तो एक विशेष चरित्र होने पर, separatorविशेषाधिकार वृद्धि हमलों के लिए कमजोर हो सकता है।
थांग फाम

1
यह वास्तव में बुरी सलाह है, आपके स्ट्रिंग में ऐसा हो सकता है ;जो आपके ऐप को तोड़ देगा।
एगिलोब

9

ऐसा लगता है कि किसी भी उत्तर ने @ElementCollectionमैपिंग के लिए सबसे महत्वपूर्ण सेटिंग्स का पता नहीं लगाया ।

जब आप इस एनोटेशन के साथ एक सूची बनाते हैं, और जेपीए / हाइबरनेट ऑटो को टेबल, कॉलम, आदि उत्पन्न करते हैं, तो यह ऑटो उत्पन्न नामों का भी उपयोग करेगा।

तो, आइए एक मूल उदाहरण का विश्लेषण करें:

@Entity
@Table(name = "sample")
public class MySample {

    @Id
    @GeneratedValue
    private Long id;

    @ElementCollection // 1
    @CollectionTable(name = "my_list", joinColumns = @JoinColumn(name = "id")) // 2
    @Column(name = "list") // 3
    private List<String> list;

}
  1. मूल @ElementCollectionएनोटेशन (जहां आप ज्ञात fetchऔर targetClassवरीयताओं को परिभाषित कर सकते हैं)
  2. @CollectionTableएनोटेशन बहुत उपयोगी है जब यह करने के साथ ही तालिका उत्पन्न हो जाएगा करने के लिए एक नाम देने के लिए, की तरह परिभाषाओं आता है joinColumns, foreignKeyके, indexes, uniqueConstraints, आदि
  3. @Columnस्तंभ के नाम को परिभाषित करने के लिए महत्वपूर्ण है varcharजो सूची के मूल्य को संग्रहीत करेगा ।

उत्पन्न डीडीएल निर्माण होगा:

-- table sample
CREATE TABLE sample (
  id bigint(20) NOT NULL AUTO_INCREMENT,
  PRIMARY KEY (id)
);

-- table my_list
CREATE TABLE IF NOT EXISTS my_list (
  id bigint(20) NOT NULL,
  list varchar(255) DEFAULT NULL,
  FOREIGN KEY (id) REFERENCES sample (id)
);

4
मुझे यह समाधान पसंद है क्योंकि यह एकमात्र प्रस्तावित समाधान है जो टेबल संरचनाओं सहित पूरा विवरण देता है और बताता है कि हमें अलग-अलग एनोटेशन की आवश्यकता क्यों है।
जुलिएन क्रोनग

6

ठीक है, मैं इसकी थोड़ी देर जानता हूं। लेकिन उन बहादुर आत्माओं के लिए जो समय बीतने के साथ इसे देखेंगे।

जैसा कि प्रलेखन में लिखा गया है :

@ बासिक: डेटाबेस कॉलम में मैपिंग का सबसे सरल प्रकार। बेसिक एनोटेशन को निम्न प्रकारों में से किसी एक की लगातार संपत्ति या उदाहरण चर पर लागू किया जा सकता है: जावा आदिम प्रकार, [...], एनमूम्स, और किसी भी अन्य प्रकार जो java.io.Serializable लागू करता है।

महत्वपूर्ण हिस्सा प्रकार है जो सीरियल को लागू करता है

अब तक समाधान का उपयोग करने के लिए सबसे सरल और सबसे आसान है केवल सूची (या किसी सीरियल करने योग्य कंटेनर) के बजाय ArrayList का उपयोग करना:

@Basic
ArrayList<Color> lovedColors;

@Basic
ArrayList<String> catNames;

हालाँकि याद रखें कि यह सिस्टम क्रमांकन का उपयोग करेगा, इसलिए यह कुछ मूल्य के साथ आएगा, जैसे:

  • यदि क्रमबद्ध ऑब्जेक्ट मॉडल बदल जाएगा, तो आप डेटा को पुनर्स्थापित करने में सक्षम नहीं हो सकते हैं

  • छोटे ओवरहेड को संग्रहीत प्रत्येक तत्व के लिए जोड़ा जाता है।

संक्षेप में

यह झंडे या कुछ तत्वों को संग्रहीत करने के लिए काफी सरल है, लेकिन मैं डेटा को संग्रहीत करने के लिए इसे दोबारा नहीं लिखूंगा जो बड़े हो सकते हैं।


यह कोशिश की, लेकिन sql तालिका ने डेटाटाइप को एक छोटे स्तर का बना दिया। क्या यह बहुत असुविधाजनक तार की सूची को सम्मिलित और पुनः प्राप्त नहीं करता है? या जेपीए स्वचालित रूप से आपके लिए सीरियल और डिसेर्बलाइज करता है?
दिज़ो

3

थियागो उत्तर सही है, प्रश्न में नमूने को और अधिक विशिष्ट जोड़कर, @ElementCollection आपके डेटाबेस में नई तालिका बनाएगा, लेकिन दो तालिकाओं को मैप किए बिना, इसका मतलब है कि संग्रह संस्थाओं का संग्रह नहीं है, बल्कि सरल प्रकारों (स्ट्रिंग्स, आदि) का संग्रह है ।) या एम्बेड करने योग्य तत्वों का संग्रह (वर्ग @ के साथ एनोटेट किया गया था )।

यहाँ स्ट्रिंग की सूची को जारी रखने का नमूना है

@ElementCollection
private Collection<String> options = new ArrayList<String>();

यहां कस्टम ऑब्जेक्ट की सूची जारी रखने के लिए नमूना है

@Embedded
@ElementCollection
private Collection<Car> carList = new ArrayList<Car>();

इस मामले के लिए हमें वर्ग एंबेडेबल बनाने की आवश्यकता है

@Embeddable
public class Car {
}

3

यहाँ @Converter और StringTokenizer का उपयोग करके सेट को संग्रहीत करने के लिए समाधान है। @ जोंक-वैन-डेर-कोगेल समाधान के खिलाफ थोड़ा और जांच ।

आपके इकाई वर्ग में:

@Convert(converter = StringSetConverter.class)
@Column
private Set<String> washSaleTickers;

StringSetConverter:

package com.model.domain.converters;

import javax.persistence.AttributeConverter;
import javax.persistence.Converter;
import java.util.HashSet;
import java.util.Set;
import java.util.StringTokenizer;

@Converter
public class StringSetConverter implements AttributeConverter<Set<String>, String> {
    private final String GROUP_DELIMITER = "=IWILLNEVERHAPPEN=";

    @Override
    public String convertToDatabaseColumn(Set<String> stringList) {
        if (stringList == null) {
            return new String();
        }
        return String.join(GROUP_DELIMITER, stringList);
    }

    @Override
    public Set<String> convertToEntityAttribute(String string) {
        Set<String> resultingSet = new HashSet<>();
        StringTokenizer st = new StringTokenizer(string, GROUP_DELIMITER);
        while (st.hasMoreTokens())
            resultingSet.add(st.nextToken());
        return resultingSet;
    }
}

1

इस मुद्दे के लिए मेरा फ़िक्स विदेशी कुंजी के साथ प्राथमिक कुंजी को अलग करना था। यदि आप ग्रहण का उपयोग कर रहे हैं और उपरोक्त बदलाव किए हैं तो कृपया डेटाबेस एक्सप्लोरर को रीफ्रेश करना याद रखें। फिर तालिकाओं से संस्थाओं को फिर से बनाएँ।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.