jpa - जेपीए EntityManager: मर्ज() पर लगातार() का उपयोग क्यों करें?




merge persist (10)

जेपीए जावा प्लेटफ़ॉर्म पर बनाए गए एंटरप्राइज़ अनुप्रयोगों के डोमेन में निर्विवाद रूप से एक महान सरलीकरण है। एक डेवलपर के रूप में जो जे 2 ईई में पुरानी इकाई सेम की जटिलताओं का सामना करना पड़ा, मैं जावा ईई विनिर्देशों के बीच जेपीए को एक बड़े छलांग के रूप में शामिल करने के रूप में देखता हूं। हालांकि, जेपीए विवरणों में गहराई से पता लगाने के दौरान मुझे ऐसी चीजें मिलती हैं जो इतनी आसान नहीं हैं। इस आलेख में मैं EntityManager के विलय और निरंतर विधियों की तुलना के साथ सौदा करता हूं जिनके अतिव्यापी व्यवहार न केवल नौसिखिया के लिए भ्रम पैदा कर सकते हैं। इसके अलावा मैं एक सामान्यीकरण का प्रस्ताव करता हूं जो एक और सामान्य विधि के विशेष मामलों के रूप में दोनों तरीकों को देखता है।

स्थायी संस्थाएं

विलय विधि के विपरीत, निरंतर विधि बहुत सरल और सहज है। निरंतर विधि के उपयोग का सबसे आम परिदृश्य निम्नानुसार समझा जा सकता है:

"इकाई वर्ग का एक नव निर्मित उदाहरण निरंतर विधि में पारित किया जाता है। इस विधि के बाद, इकाई को प्रबंधित किया जाता है और डेटाबेस में सम्मिलन के लिए योजना बनाई जाती है। यह लेनदेन पर या उससे पहले हो सकता है या जब फ्लश विधि कहा जाता है। यदि इकाई व्यक्तिगत कैस्केड रणनीति के साथ चिह्नित रिश्ते के माध्यम से किसी अन्य इकाई का संदर्भ देती है तो इस प्रक्रिया को भी लागू किया जाता है। "

विनिर्देश विवरण में अधिक जाता है, हालांकि, उन्हें याद रखना महत्वपूर्ण नहीं है क्योंकि इन विवरणों में केवल कम या ज्यादा विदेशी परिस्थितियां शामिल हैं।

विलय संस्थाएं

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

जेपीए विनिर्देश से अनुच्छेदों को दोहराने के बजाय मैंने एक प्रवाह आरेख तैयार किया है जो स्कीमेटिक रूप से मर्ज विधि के व्यवहार को दर्शाता है:

तो, मुझे कब जारी रहना चाहिए और विलय कब करना चाहिए?

दृढ़ रहना

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

विलय

  • आप चाहते हैं कि विधि या तो डेटाबेस में किसी इकाई को सम्मिलित या अद्यतन करे।
  • आप इकाइयों को एक स्टेटलेस तरीके से संभालना चाहते हैं (सेवाओं में डेटा ट्रांसफर ऑब्जेक्ट्स)
  • आप एक नई इकाई डालना चाहते हैं जिसमें किसी अन्य इकाई का संदर्भ हो जो हो सकता है लेकिन अभी तक नहीं बनाया जा सकता है (रिश्ते को MERGE चिह्नित किया जाना चाहिए)। उदाहरण के लिए, एक नया या एक पूर्ववर्ती एल्बम के संदर्भ के साथ एक नई तस्वीर डालना।

EntityManager.merge() नई ऑब्जेक्ट्स डाल सकता है और मौजूदा अपडेट कर सकता है।

क्यों एक persist() का उपयोग करना चाहते हैं (जो केवल नई वस्तुओं को बना सकते हैं)?


उत्तरों के माध्यम से 'कैस्केड' और आईडी पीढ़ी के बारे में कुछ विवरण गायब हैं। सवाल देखें

साथ ही, यह उल्लेखनीय है कि आप विलय और बने रहने के लिए अलग-अलग Cascade एनोटेशन कर सकते हैं: Cascade.MERGE और Cascade Cascade.PERSIST जिसे उपयोग की गई विधि के अनुसार माना जाएगा।

कल्पना आपका दोस्त है;)


जब आप लगातार उपयोग करते हैं और मर्ज का उपयोग कब करते हैं, तो आप सलाह के लिए यहां आ सकते हैं। मुझे लगता है कि यह स्थिति पर निर्भर करता है: आपको एक नया रिकॉर्ड बनाने की आवश्यकता है और यह लगातार डेटा पुनर्प्राप्त करना कितना मुश्किल है।

मान लें कि आप एक प्राकृतिक कुंजी / पहचानकर्ता का उपयोग कर सकते हैं।

  • डेटा को जारी रखने की जरूरत है, लेकिन एक बार में एक रिकॉर्ड मौजूद है और एक अद्यतन के लिए कहा जाता है। इस मामले में आप एक persist कोशिश कर सकते हैं और अगर यह EntityExistsException फेंकता है, तो आप इसे देखते हैं और डेटा को गठबंधन करते हैं:

    {entityManager.persist (इकाई)} आज़माएं

    पकड़ो (EntityExistsException अपवाद) {/ * पुनर्प्राप्त करें और विलय करें * /}

  • स्थगित डेटा को अद्यतन करने की आवश्यकता है, लेकिन थोड़ी देर में डेटा के लिए अभी तक कोई रिकॉर्ड नहीं है। इस मामले में आप इसे देखते हैं, और अगर इकाई गुम है तो जारी रहें:

    इकाई = इकाई प्रबंधक। फ़ाइल (कुंजी);

    अगर (इकाई == शून्य) {entityManager.persist (इकाई); }

    अन्य {/ * विलय * /}

यदि आपके पास प्राकृतिक कुंजी / पहचानकर्ता नहीं है, तो यह पता लगाने में कठिनाई होगी कि इकाई मौजूद है या नहीं, या इसे कैसे देखना है।

विलयों को दो तरीकों से भी निपटाया जा सकता है:

  1. यदि परिवर्तन आमतौर पर छोटे होते हैं, तो उन्हें प्रबंधित इकाई पर लागू करें।
  2. यदि परिवर्तन आम हैं, तो आईडी को लगातार इकाई से कॉपी करें, साथ ही साथ अनचाहे डेटा। फिर पुरानी सामग्री को प्रतिस्थापित करने के लिए EntityManager :: merge () को कॉल करें।

जेपीए विनिर्देश निम्नलिखित के बारे में निम्नलिखित कहते persist()

यदि एक्स एक अलग वस्तु है, तो EntityExistsException फेंक दिया जा सकता है जब लगातार ऑपरेशन लागू किया जाता है, या EntityExistsException या अन्य PersistenceException फ़्लश या प्रतिबद्ध समय पर फेंक दिया जा सकता है।

इसलिए persist() का उपयोग उपयुक्त होगा जब ऑब्जेक्ट को एक अलग वस्तु नहीं होना चाहिए । आप कोड को PersistenceException फेंकना पसंद कर सकते हैं ताकि यह तेजी से विफल हो जाए।

हालांकि विनिर्देश अस्पष्ट है , persist() किसी ऑब्जेक्ट के लिए @GeneratedValue @Id सेट कर सकता है। merge() हालांकि पहले से जेनरेट @Id साथ एक ऑब्जेक्ट होना चाहिए।


दृढ़ता और विलय दो अलग-अलग उद्देश्यों के लिए हैं (वे विकल्प बिल्कुल नहीं हैं)।

(मतभेद जानकारी विस्तारित करने के लिए संपादित)

जारी रहती है:

  • डेटाबेस में एक नया रजिस्टर डालें
  • इकाई प्रबंधक को ऑब्जेक्ट संलग्न करें।

विलय:

  • एक ही आईडी के साथ संलग्न वस्तु खोजें और इसे अपडेट करें।
  • यदि अद्यतन मौजूद है और पहले से संलग्न वस्तु को वापस कर दें।
  • यदि मौजूद नहीं है तो डेटाबेस में नया रजिस्टर डालें।

बने रहें () दक्षता:

  • मर्ज () से डेटाबेस में नया रजिस्टर डालने के लिए यह अधिक कुशल हो सकता है।
  • यह मूल वस्तु को डुप्लिकेट नहीं करता है।

बने रहें () अर्थशास्त्र:

  • यह सुनिश्चित करता है कि आप गलती से जोड़ रहे हैं और अपडेट नहीं कर रहे हैं।

उदाहरण:

{
    AnyEntity newEntity;
    AnyEntity nonAttachedEntity;
    AnyEntity attachedEntity;

    // Create a new entity and persist it        
    newEntity = new AnyEntity();
    em.persist(newEntity);

    // Save 1 to the database at next flush
    newEntity.setValue(1);

    // Create a new entity with the same Id than the persisted one.
    AnyEntity nonAttachedEntity = new AnyEntity();
    nonAttachedEntity.setId(newEntity.getId());

    // Save 2 to the database at next flush instead of 1!!!
    nonAttachedEntity.setValue(2);
    attachedEntity = em.merge(nonAttachedEntity);

    // This condition returns true
    // merge has found the already attached object (newEntity) and returns it.
    if(attachedEntity==newEntity) {
            System.out.print("They are the same object!");
    }

    // Set 3 to value
    attachedEntity.setValue(3);
    // Really, now both are the same object. Prints 3
    System.out.println(newEntity.getValue());

    // Modify the un attached object has no effect to the entity manager
    // nor to the other objects
    nonAttachedEntity.setValue(42);
}

इस तरह इकाई प्रबंधक में किसी भी रजिस्टर के लिए केवल 1 संलग्न वस्तु मौजूद है।

एक आईडी के साथ एक इकाई के लिए विलय () कुछ ऐसा है:

AnyEntity myMerge(AnyEntity entityToSave) {
    AnyEntity attached = em.find(AnyEntity.class, entityToSave.getId());
    if(attached==null) {
            attached = new AnyEntity();
            em.persist(attached);
    }
    BeanUtils.copyProperties(attached, entityToSave);

    return attached;
}

यद्यपि यदि MySQL विलय () से कनेक्ट किया गया है, तो ऑन डिप्लिकेट कुंजी अपडेट विकल्प के साथ INSERT को कॉल का उपयोग करके लगातार () के रूप में सक्षम हो सकता है, जेपीए एक बहुत ही उच्च स्तरीय प्रोग्रामिंग है और आप यह नहीं मान सकते कि यह हर जगह मामला होगा।


परिदृश्य एक्स:

सारणी: स्पिटर (एक), सारणी: स्पिटल (कई) (स्पिटल्स एफके के साथ रिश्ते का मालिक है: spitter_id)

इस परिदृश्य में बचत में परिणाम होता है: स्पिटर और दोनों स्पिटल जैसे कि समान स्पिटर के स्वामित्व में।

        Spitter spitter=new Spitter();  
    Spittle spittle3=new Spittle();     
    spitter.setUsername("George");
    spitter.setPassword("test1234");
    spittle3.setSpittle("I love java 2");       
    spittle3.setSpitter(spitter);               
    dao.addSpittle(spittle3); // <--persist     
    Spittle spittle=new Spittle();
    spittle.setSpittle("I love java");
    spittle.setSpitter(spitter);        
    dao.saveSpittle(spittle); //<-- merge!!

परिदृश्य वाई:

यह स्पिटर को बचाएगा, 2 स्पिटल को बचाएगा लेकिन वे एक ही स्पिटर का संदर्भ नहीं देंगे!

        Spitter spitter=new Spitter();  
    Spittle spittle3=new Spittle();     
    spitter.setUsername("George");
    spitter.setPassword("test1234");
    spittle3.setSpittle("I love java 2");       
    spittle3.setSpitter(spitter);               
    dao.save(spittle3); // <--merge!!       
    Spittle spittle=new Spittle();
    spittle.setSpittle("I love java");
    spittle.setSpitter(spitter);        
    dao.saveSpittle(spittle); //<-- merge!!

मैं अपनी इकाई पर आलसी लोडिंग अपवाद प्राप्त कर रहा था क्योंकि मैं सत्र में एक आलसी लोड किए गए संग्रह तक पहुंचने का प्रयास कर रहा था।

मैं एक अलग अनुरोध में क्या करूँगा, सत्र से इकाई को पुनः प्राप्त करें और फिर मेरे जेएसपी पेज में एक संग्रह तक पहुंचने का प्रयास करें जो समस्याग्रस्त था।

इसे कम करने के लिए, मैंने अपने नियंत्रक में एक ही इकाई को अपडेट किया और इसे मेरे जेएसपी में पास कर दिया, हालांकि मुझे लगता है कि जब मैंने सत्र में फिर से सहेजा था कि यह LazyLoadingException बावजूद भी पहुंच योग्य होगा और LazyLoadingException फेंक नहीं LazyLoadingException , उदाहरण 2 का एक संशोधन:

निम्नलिखित ने मेरे लिए काम किया है:

// scenario 2 MY WAY
// tran starts
e = new MyEntity();
e = em.merge(e); // re-assign to the same entity "e"

//access e from jsp and it will work dandy!!

मैंने देखा कि जब मैंने em.merge उपयोग किया em.merge , तो मुझे प्रत्येक INSERT लिए एक SELECT कथन मिला, भले ही जेपीए मेरे लिए कोई फ़ील्ड नहीं बना रहा था - प्राथमिक कुंजी फ़ील्ड एक यूयूआईडी था जिसे मैंने स्वयं सेट किया था। मैंने em.persist(myEntityObject) स्विच किया और फिर केवल INSERT कथन प्राप्त कर लिया।


विलय के बारे में कुछ और विवरण जो आपको लगातार बने रहने में मदद करेंगे:

मूल इकाई के अलावा एक प्रबंधित उदाहरण लौटाना विलय प्रक्रिया का एक महत्वपूर्ण हिस्सा है। यदि एक ही पहचानकर्ता के साथ एक इकाई उदाहरण दृढ़ता संदर्भ में पहले से मौजूद है, तो प्रदाता अपने राज्य को विलय किए जा रहे इकाई की स्थिति के साथ ओवरराइट कर देगा, लेकिन पहले से मौजूद प्रबंधित संस्करण क्लाइंट को वापस लौटाया जाना चाहिए ताकि यह हो सके उपयोग किया गया। यदि प्रदाता ने दृढ़ता संदर्भ में कर्मचारी उदाहरण को अद्यतन नहीं किया है, तो उस घटना के किसी भी संदर्भ में नए राज्य में विलय होने के साथ असंगत हो जाएगा।

जब एक नई इकाई पर विलय () को उजागर किया जाता है, तो यह लगातार () ऑपरेशन के समान व्यवहार करता है। यह इकाई को दृढ़ता संदर्भ में जोड़ता है, लेकिन मूल इकाई उदाहरण जोड़ने के बजाय, यह एक नई प्रति बनाता है और इसके बजाए उस उदाहरण का प्रबंधन करता है। मर्ज () ऑपरेशन द्वारा बनाई गई प्रतिलिपि जारी है जैसे कि जारी () विधि को उस पर लागू किया गया था।

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

आलसी लोडिंग रिश्तों मर्ज ऑपरेशन में एक विशेष मामला है। यदि एक आलसी लोडिंग रिलेशनशिप एक इकाई पर अलग होने से पहले ट्रिगर नहीं किया गया था, तो इकाई को विलय होने पर उस संबंध को अनदेखा कर दिया जाएगा। यदि रिश्ते को प्रबंधित करते समय ट्रिगर किया गया था और फिर इकाई को अलग करते समय शून्य पर सेट किया गया था, तो इकाई के प्रबंधित संस्करण को विलय के दौरान संबंधों को भी मंजूरी दे दी जाएगी। "

उपर्युक्त सभी जानकारी माइक कीथ और मेरिक Schnicariol द्वारा "प्रो जेपीए 2 जावा ™ पर्सिस्टेंस एपीआई मास्टरिंग" से ली गई थी। अध्याय 6. खंड अलगाव और विलय। यह पुस्तक वास्तव में लेखकों द्वारा जेपीए को समर्पित दूसरी पुस्तक है। इस नई किताब में पहले नई जानकारी है। मैं वास्तव में उन लोगों के लिए इस पुस्तक को पढ़ने के लिए पुनः संयोजित हूं जो जेपीए के साथ गंभीरता से शामिल होंगे। मुझे अपने पहले जवाब पोस्ट करने के लिए खेद है।


merge और persist बीच कुछ और अंतर persist (मैं उन लोगों को फिर से बताऊंगा जो पहले से ही यहां पोस्ट किए गए हैं):

डी 1। merge से गुजरने वाली इकाई को प्रबंधित नहीं किया जाता है, बल्कि प्रबंधित होने वाला एक और उदाहरण देता है। दूसरी तरफ persist उत्तीर्ण इकाई को प्रबंधित करेंगे:

//MERGE: passedEntity remains unmanaged, but newEntity will be managed
Entity newEntity = em.merge(passedEntity);

//PERSIST: passedEntity will be managed after this
em.persist(passedEntity);

डी 2। यदि आप एक इकाई को हटाते हैं और फिर इकाई को पीछे छोड़ने का फैसला करते हैं, तो आप केवल उन्हीं () के साथ ऐसा कर सकते हैं, क्योंकि merge एक IllegalArgumentException फेंक देगा।

डी 3। यदि आपने मैन्युअल रूप से अपनी आईडी का ख्याल रखने का निर्णय लिया है (उदाहरण के लिए यूयूआईडी का उपयोग करके), तो एक merge ऑपरेशन उस आईडी के साथ मौजूदा इकाइयों को देखने के लिए बाद के SELECT प्रश्नों को ट्रिगर करेगा, जबकि persist उन प्रश्नों की आवश्यकता नहीं हो सकती है।

D4। ऐसे मामले हैं जब आप अपने कोड को कॉल करने वाले कोड पर भरोसा नहीं करते हैं, और यह सुनिश्चित करने के लिए कि कोई डेटा अपडेट नहीं किया गया है, बल्कि इसे डाला गया है, तो आपको persist उपयोग करना होगा।





java-persistence-api