हाइबरनेट में अलग वस्तुओं को फिर से संलग्न करने का उचित तरीका क्या है?


186

मेरे पास एक ऐसी स्थिति है जिसमें मुझे अलग-अलग वस्तुओं को एक हाइबरनेट सत्र में फिर से संलग्न करने की आवश्यकता है, हालांकि उसी पहचान का एक ऑब्जेक्ट पहले से ही सत्र में मौजूद है, जो त्रुटियों का कारण होगा।

अभी, मैं दो चीजों में से एक कर सकता हूं।

  1. getHibernateTemplate().update( obj ) यह काम करता है अगर और केवल अगर कोई वस्तु हाइबरनेट सत्र में पहले से मौजूद नहीं है। अपवाद तब दिए गए पहचानकर्ता के साथ किसी वस्तु को कहते हैं जिसे सत्र में पहले से मौजूद है, जब मुझे बाद में इसकी आवश्यकता होती है।

  2. getHibernateTemplate().merge( obj ) यह काम करता है अगर और केवल अगर कोई वस्तु हाइबरनेट सत्र में मौजूद है। अपवाद तब फेंके जाते हैं जब मुझे एक सत्र में वस्तु का उपयोग करने की आवश्यकता होती है यदि मैं इसका उपयोग करता हूं।

इन दो परिदृश्यों को देखते हुए, मैं सामान्य रूप से वस्तुओं को सत्र कैसे दे सकता हूं? मैं इस समस्या के समाधान के प्रवाह को नियंत्रित करने के लिए अपवादों का उपयोग नहीं करना चाहता, क्योंकि एक और अधिक सुंदर समाधान होना चाहिए ...

जवाबों:


181

तो ऐसा लगता है कि JPA में एक बासी अलग इकाई को फिर से बनाने का कोई तरीका नहीं है।

merge() डीबी को बासी स्थिति को धक्का देगा, और किसी भी हस्तक्षेप करने वाले अपडेट को अधिलेखित करेगा।

refresh() एक अलग इकाई पर नहीं बुलाया जा सकता है।

lock() एक अलग इकाई पर नहीं बुलाया जा सकता है, और यहां तक ​​कि अगर यह हो सकता है, और यह इकाई को फिर से चालू किया, 'लॉक' को तर्क के साथ 'LockMode.NONE' कहते हुए यह अर्थ लगाया कि आप लॉक कर रहे हैं, लेकिन लॉक नहीं कर रहे हैं, एपीआई डिजाइन का सबसे प्रतिगामी टुकड़ा है मैंने कभी देखा है।

तो तुम फंस गए। एक detach()विधि है, लेकिन नहीं attach()या reattach()। ऑब्जेक्ट जीवनचक्र में एक स्पष्ट कदम आपके लिए उपलब्ध नहीं है।

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

ऐसा लगता है कि ऐसा करने का एकमात्र तरीका यह है कि आप अपनी बासी अलग हो चुकी इकाई को छोड़ दें और उसी आईडी के साथ एक खोज क्वेरी करें, जो L2 या DB को हिट करेगा।

Mik


1
मुझे आश्चर्य है कि क्या कोई कारण है कि जेपीए की युक्ति refresh()अलग-अलग संस्थाओं पर अनुमति नहीं देती है? 2.0 युक्ति के माध्यम से देखने से मुझे कोई औचित्य नहीं दिखता है; बस इसकी अनुमति नहीं है।
FGreg

11
यह निश्चित रूप से सटीक नहीं है। JPwH से: *Reattaching a modified detached instance* A detached instance may be reattached to a new Session (and managed by this new persistence context) by calling update() on the detached object. In our experience, it may be easier for you to understand the following code if you rename the update() method in your mind to reattach()—however, there is a good reason it’s called updating.अधिक में खंड 9.3.2 पाया जा सकता है
cwash

लगातार ऑब्जेक्ट महान काम करते हैं, गंदे झंडे को प्रारंभिक भार और मूल्य (ओं) के बीच फ्लश () समय पर डेल्टा के आधार पर सेट किया जाता है। अलग किए गए ऑब्जेक्ट की आवश्यकता है, और वर्तमान में यह कार्यक्षमता नहीं है। यह करने के लिए हाइबरनेट करने का तरीका अलग वस्तुओं के लिए एक अतिरिक्त हैश / आईडी जोड़ना है। और अलग वस्तु की अंतिम स्थिति का एक स्नैपशॉट उपलब्ध रखें, जैसे वे लगातार वस्तुओं के लिए करते हैं। इसलिए वे उस सभी मौजूदा कोड का लाभ उठा सकते हैं और अलग वस्तुओं के लिए काम कर सकते हैं। इस तरह @mikhailfranco ने उल्लेख किया है कि हम "बासी स्थिति को डीबी में नहीं धकेलेंगे, और किसी भी हस्तक्षेप करने वाले अपडेट को ओवरराइट करेंगे"
टॉम

2
हाइबरनेट जेवाडॉक (लेकिन जेपीए नहीं) के अनुसार, lock(LockMode.NONE)वास्तव में एक क्षणिक वस्तु पर कॉल किया जा सकता है, और यह इकाई को सत्र में पुनः वितरित करता है। देखें stackoverflow.com/a/3683370/14379
शॉन

लॉक ने मेरे लिए काम नहीं किया: java.lang.IllegalArgumentException: org.hibernate.internal.SessionImpl.lock (SessionImpl.java .3491) पर org.hibernate.internal.SessionImpl.lock (SessionImpl) में दृढ़ता के संदर्भ में इकाई नहीं। जावा: 3482) com.github.vok.framework.DisableTransactionControlEMDelegate.lock पर (DB.kt)
मार्टिन Vysny

32

इन सभी उत्तरों में एक महत्वपूर्ण अंतर है। अद्यतन () का उपयोग (पुनः) एक सत्र में अपनी वस्तु ग्राफ को संलग्न करने के लिए किया जाता है। जिन वस्तुओं को आप पास करते हैं, वे वही हैं जिन्हें प्रबंधित किया जाता है।

मर्ज () वास्तव में लगाव एपीआई नहीं है। सूचना मर्ज () का रिटर्न मान है? ऐसा इसलिए है क्योंकि यह आपको प्रबंधित ग्राफ़ लौटाता है, जो आपके द्वारा पास किया गया ग्राफ़ नहीं हो सकता है। मर्ज () एक जेपीए एपीआई है और इसका व्यवहार जेपीए की कल्पना से संचालित होता है। यदि आप जिस ऑब्जेक्ट को मर्ज करने के लिए पास करते हैं () पहले से ही प्रबंधित है (पहले से ही सत्र से जुड़ा हुआ है) तो वह ग्राफ हाइबरनेट के साथ काम करता है; में पारित वस्तु वही वस्तु है जो मर्ज () से लौटी है। यदि, फिर भी, आप जिस ऑब्जेक्ट को मर्ज () में पास करते हैं, वह अलग हो जाता है, तो हाइबरनेट एक नया ऑब्जेक्ट ग्राफ़ बनाता है जिसे प्रबंधित किया जाता है और यह नए प्रबंधित ग्राफ़ पर आपके अलग किए गए ग्राफ़ से राज्य की प्रतिलिपि बनाता है। फिर, यह सब जेपीए की कल्पना से तय और शासित है।

"यह सुनिश्चित करें कि यह इकाई प्रबंधित है, या इसे प्रबंधित करें" के लिए एक सामान्य रणनीति के संदर्भ में, यह इस बात पर निर्भर करता है कि क्या आप अभी तक सम्मिलित नहीं किए गए डेटा को भी खाते में रखना चाहते हैं। आप ऐसा मानते हुए कुछ ऐसा प्रयोग करें

if ( session.contains( myEntity ) ) {
    // nothing to do... myEntity is already associated with the session
}
else {
    session.saveOrUpdate( myEntity );
}

सूचना मैंने अद्यतन () के बजाय saveOrUpdate () का उपयोग किया है। यदि आप नहीं चाहते हैं कि अभी तक डाला गया डेटा यहां संभाला गया है, तो इसके बजाय अपडेट () का उपयोग करें ...


3
इस सवाल का सही जवाब है - केस बंद!
cwash

2
Session.contains(Object)संदर्भ द्वारा जाँच। यदि सत्र में एक ही पंक्ति का प्रतिनिधित्व करने वाली एक और इकाई पहले से ही है और आप एक अलग उदाहरण को पारित करते हैं तो आपको एक अपवाद मिलेगा।
djmj

Session.contains(Object)संदर्भ द्वारा जाँच के रूप में , यदि सत्र में एक ही पंक्ति का प्रतिनिधित्व करने वाला एक और इकाई है, तो यह गलत है, और यह इसे अपडेट कर देगा।
AxelWass

19

अनुशासनहीन उत्तर: आप शायद एक विस्तारित दृढ़ता संदर्भ की तलाश कर रहे हैं। सीम फ्रेमवर्क के पीछे यह एक मुख्य कारण है ... यदि आप विशेष रूप से स्प्रिंग में हाइबरनेट का उपयोग करने के लिए संघर्ष कर रहे हैं, तो सीम के डॉक्स के इस टुकड़े को देखें।

कूटनीतिक उत्तर: यह हाइबरनेट डॉक्स में वर्णित है । यदि आपको और अधिक स्पष्टीकरण की आवश्यकता है, तो " निरंतर कार्यशील वस्तुओं के साथ काम करना" नामक हाइबरनेट के साथ जावा पर्सिस्टेंस की धारा 9.3.2 पर एक नज़र डालें । यदि आप हाइबरनेट के साथ CRUD से अधिक कुछ कर रहे हैं तो मैं आपको इस पुस्तक को प्राप्त करने की जोरदार सलाह दूंगा।


5
से seamframework.org : "सीवन 3 के सक्रिय विकास रेड हैट से रोक दिया गया है।" लिंक "सीवन के डॉक्स के इस टुकड़े" भी मर चुका है।
बैडबिशप

14

यदि आप सुनिश्चित हैं कि आपकी इकाई को संशोधित नहीं किया गया है (या यदि आप सहमत हैं कि कोई संशोधन खो जाएगा), तो आप इसे लॉक के साथ सत्र में पुनः भेज सकते हैं।

session.lock(entity, LockMode.NONE);

यह कुछ भी लॉक नहीं करेगा, लेकिन यह सत्र कैश से इकाई प्राप्त करेगा (या यदि वहां नहीं मिला है) तो इसे डीबी से पढ़ें।

जब आप एक "पुराने" (उदाहरण के लिए HttpSession से) संस्थाओं से संबंधों को नेविगेट कर रहे हों तो LazyInitException को रोकना बहुत उपयोगी है। आप पहले इकाई को "पुनः संलग्न" करते हैं।

जब आपको विरासत में मिला मैप (जो पहले से ही गेटआईड (अपवाद) पर अपवाद छोड़ देगा) को छोड़कर, प्राप्त करना भी काम कर सकता है)

entity = session.get(entity.getClass(), entity.getId());

2
मैं सत्र के साथ एक इकाई को फिर से जोड़ना चाहूंगा। दुर्भाग्य से, Session.lock(entity, LockMode.NONE)अपवाद कहने में विफल रहता है: अनैतिक रूप से क्षणिक संग्रह को पुन: व्यवस्थित नहीं कर सका। इससे कैसे उबर सकते हैं?
dma_k

1
वास्तव में मैं पूरी तरह से सही नहीं था। लॉक का उपयोग करके () अपनी इकाई को पुन: सेट करता है, लेकिन इसके लिए बाध्य अन्य संस्थाएं नहीं। इसलिए यदि आप इकाई .getOtherEntity ()। GetYetAnotherEntity () करते हैं, तो आपके पास एक LazyInit अपवाद हो सकता है। एकमात्र तरीका जो मुझे पता है कि इसे खोजने के लिए उपयोग करना है। Unit = em.find (एंटिटी .getClass (), unit.getId ();
जॉन रिज़ो

कोई Session.find()एपीआई विधि नहीं है। शायद आपका मतलब है Session.load(Object object, Serializable id)
dma_k

11

चूंकि यह एक बहुत ही सामान्य प्रश्न है, इसलिए मैंने यह लेख लिखा है , जिस पर यह उत्तर आधारित है।

इकाई राज्यों

जेपीए निम्नलिखित इकाई राज्यों को परिभाषित करता है:

नया (क्षणिक)

एक नई बनाई गई वस्तु जो कभी भी एक हाइबरनेट Session(उर्फ Persistence Context) के साथ संबद्ध नहीं हुई है और किसी भी डेटाबेस तालिका पंक्ति में मैप नहीं की गई है जिसे नई (क्षणिक) स्थिति में माना जाता है।

लगातार बने रहने के लिए हमें या तो स्पष्ट रूप से EntityManager#persistविधि को बुलाना होगा या सकर्मक दृढ़ता का प्रयोग करना होगा।

स्थायी (प्रबंधित)

एक स्थायी इकाई को डेटाबेस तालिका पंक्ति के साथ जोड़ा गया है और इसे वर्तमान में चल रहे परिप्रेक्ष्य द्वारा प्रबंधित किया जा रहा है। ऐसी इकाई में किए गए किसी भी परिवर्तन को डेटाबेस में (सत्र फ्लश-टाइम के दौरान) पहचाना और प्रचारित किया जा रहा है।

हाइबरनेट के साथ, हमें अब INSERT / UPDATE / DELETE कथनों को निष्पादित नहीं करना है। हाइबरनेट एक व्यवहारिक लिखने के पीछे काम करने की शैली को नियोजित करता है और वर्तमान Sessionफ्लश-टाइम के दौरान परिवर्तनों को अंतिम अंतिम क्षण में सिंक्रनाइज़ किया जाता है ।

जुदा जुदा

एक बार वर्तमान में चल रहे पर्सेंटेज कॉन्सेप्ट को बंद करने के बाद सभी पहले से प्रबंधित इकाइयां अलग हो जाती हैं। क्रमिक परिवर्तनों को अब ट्रैक नहीं किया जाएगा और कोई स्वचालित डेटाबेस सिंक्रनाइज़ेशन नहीं होने वाला है।

इकाई अवस्था परिवर्तन

आप EntityManagerइंटरफ़ेस द्वारा परिभाषित विभिन्न विधियों का उपयोग करके इकाई स्थिति को बदल सकते हैं ।

जेपीए इकाई की स्थिति को बेहतर ढंग से समझने के लिए, निम्नलिखित चित्र पर विचार करें:

जेपीए इकाई राज्य परिवर्तन

JPA का उपयोग करते समय, एक सक्रिय करने के लिए एक अलग इकाई को पुन: व्यवस्थित करने के लिए EntityManager, आप मर्ज ऑपरेशन का उपयोग कर सकते हैं ।

मूल हाइबरनेट एपीआई का उपयोग करते समय, इसके अलावा merge, आप एक अलग इकाई को एक सक्रिय हाइबरनेट को अपडेट करने के लिए अलग कर सकते हैं, जैसा कि निम्नलिखित चित्र द्वारा दिखाया गया है:

हाइबरनेट इकाई राज्य संक्रमण

एक अलग इकाई विलय

मर्ज किसी निकाय निकाय स्थिति (स्रोत) की प्रतिलिपि किसी प्रबंधित निकाय आवृत्ति (गंतव्य) पर करने जा रहा है।

विचार करें कि हमने निम्नलिखित Bookइकाई को बनाए रखा है , और अब इकाई को अलग EntityManagerकर दिया गया है क्योंकि इकाई को बंद रखने के लिए इसका उपयोग किया गया था:

Book _book = doInJPA(entityManager -> {
    Book book = new Book()
    .setIsbn("978-9730228236")
    .setTitle("High-Performance Java Persistence")
    .setAuthor("Vlad Mihalcea");

    entityManager.persist(book);

    return book;
});

जबकि इकाई अलग अवस्था में है, हम इसे निम्नानुसार संशोधित करते हैं:

_book.setTitle(
    "High-Performance Java Persistence, 2nd edition"
);

अब, हम डेटाबेस में परिवर्तन करना चाहते हैं, इसलिए हम mergeविधि को कॉल कर सकते हैं :

doInJPA(entityManager -> {
    Book book = entityManager.merge(_book);

    LOGGER.info("Merging the Book entity");

    assertFalse(book == _book);
});

और हाइबरनेट निम्नलिखित SQL कथनों को निष्पादित करने जा रहा है:

SELECT
    b.id,
    b.author AS author2_0_,
    b.isbn AS isbn3_0_,
    b.title AS title4_0_
FROM
    book b
WHERE
    b.id = 1

-- Merging the Book entity

UPDATE
    book
SET
    author = 'Vlad Mihalcea',
    isbn = '978-9730228236',
    title = 'High-Performance Java Persistence, 2nd edition'
WHERE
    id = 1

यदि विलय इकाई में वर्तमान में कोई समतुल्य नहीं है EntityManager, तो डेटाबेस से एक ताजा इकाई स्नैपशॉट प्राप्त किया जाएगा।

एक बार जब एक मानव इकाई होती है, तो जेपीए उस निकाले गए राज्य की स्थिति की प्रतिलिपि उसी पर बनाता है जिसे वर्तमान में प्रबंधित किया गया है, और हठ प्रसंग केflush दौरान , एक अद्यतन उत्पन्न किया जाएगा यदि गंदा जाँच तंत्र पाता है कि प्रबंधित इकाई बदल गई है।

इसलिए, उपयोग करते समय merge, अलग किए गए ऑब्जेक्ट का उदाहरण मर्ज ऑपरेशन के बाद भी अलग होना जारी रहेगा।

एक अलग इकाई को फिर से प्रशिक्षित करना

हाइबरनेट, लेकिन नहीं जेपीए updateविधि के माध्यम से रिटेटिंग का समर्थन करता है ।

एक हाइबरनेट Sessionकेवल एक इकाई ऑब्जेक्ट को किसी दिए गए डेटाबेस पंक्ति के लिए संबद्ध कर सकता है। ऐसा इसलिए है क्योंकि पर्सेंटेज कॉन्सेप्ट एक इन-मेमोरी कैश (प्रथम स्तर कैश) के रूप में कार्य करता है और केवल एक मान (इकाई) एक दिए गए कुंजी (इकाई प्रकार और डेटाबेस पहचानकर्ता) के साथ जुड़ा हुआ है।

एक इकाई को तब ही बदला जा सकता है जब कोई अन्य JVM ऑब्जेक्ट (समान डेटाबेस पंक्ति से मेल खाता हो) जो पहले से ही हाइबरनेट के साथ जुड़ा हुआ है Session

ध्यान में रखते हुए हमने Bookइकाई को बनाए रखा है और जब Bookइकाई अलग अवस्था में थी तब हमने इसे संशोधित किया था:

Book _book = doInJPA(entityManager -> {
    Book book = new Book()
    .setIsbn("978-9730228236")
    .setTitle("High-Performance Java Persistence")
    .setAuthor("Vlad Mihalcea");

    entityManager.persist(book);

    return book;
});

_book.setTitle(
    "High-Performance Java Persistence, 2nd edition"
);

हम इस तरह से अलग की गई इकाई को पुनः प्राप्त कर सकते हैं:

doInJPA(entityManager -> {
    Session session = entityManager.unwrap(Session.class);

    session.update(_book);

    LOGGER.info("Updating the Book entity");
});

और हाइबरनेट निम्नलिखित SQL कथन को निष्पादित करेगा:

-- Updating the Book entity

UPDATE
    book
SET
    author = 'Vlad Mihalcea',
    isbn = '978-9730228236',
    title = 'High-Performance Java Persistence, 2nd edition'
WHERE
    id = 1

updateविधि करने की आवश्यकता है एक हाइबरनेट करने के लिए ।unwrapEntityManagerSession

इसके विपरीत merge, प्रदान की गई अलग इकाई को वर्तमान दृढ़ता के संदर्भ के साथ फिर से जोड़ा जा रहा है और फ्लश के दौरान एक अद्यतन निर्धारित किया जाता है कि इकाई संशोधित हुई है या नहीं।

इसे रोकने के लिए, आप @SelectBeforeUpdateहाइबरनेट एनोटेशन का उपयोग कर सकते हैं जो कि एक सेलेक्ट स्टेटमेंट को ट्रिगर करेगा जो कि लोड की गई स्थिति को प्राप्त करता है जो फिर गंदे जाँच तंत्र द्वारा उपयोग किया जाता है।

@Entity(name = "Book")
@Table(name = "book")
@SelectBeforeUpdate
public class Book {

    //Code omitted for brevity
}

NonUniqueObjectException से सावधान रहें

इसके साथ होने वाली एक समस्या यह है कि updateअगर हठ प्रसंग में पहले से ही आईडी के साथ और निम्न उदाहरण में एक ही प्रकार के साथ एक इकाई संदर्भ शामिल है:

Book _book = doInJPA(entityManager -> {
    Book book = new Book()
    .setIsbn("978-9730228236")
    .setTitle("High-Performance Java Persistence")
    .setAuthor("Vlad Mihalcea");

    Session session = entityManager.unwrap(Session.class);
    session.saveOrUpdate(book);

    return book;
});

_book.setTitle(
    "High-Performance Java Persistence, 2nd edition"
);

try {
    doInJPA(entityManager -> {
        Book book = entityManager.find(
            Book.class,
            _book.getId()
        );

        Session session = entityManager.unwrap(Session.class);
        session.saveOrUpdate(_book);
    });
} catch (NonUniqueObjectException e) {
    LOGGER.error(
        "The Persistence Context cannot hold " +
        "two representations of the same entity",
        e
    );
}

अब, जब ऊपर परीक्षण मामले को निष्पादित किया जाता है, तो हाइबरनेट फेंकने जा रहा है NonUniqueObjectExceptionक्योंकि दूसरे में EntityManagerपहले Bookसे ही एक पहचानकर्ता के साथ एक इकाई है जिस पर हम गुजरते हैं update, और दृढ़ता संदर्भ एक ही इकाई के दो प्रतिनिधित्व नहीं कर सकता है।

org.hibernate.NonUniqueObjectException:
    A different object with the same identifier value was already associated with the session : [com.vladmihalcea.book.hpjp.hibernate.pc.Book#1]
    at org.hibernate.engine.internal.StatefulPersistenceContext.checkUniqueness(StatefulPersistenceContext.java:651)
    at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.performUpdate(DefaultSaveOrUpdateEventListener.java:284)
    at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.entityIsDetached(DefaultSaveOrUpdateEventListener.java:227)
    at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.performSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:92)
    at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.onSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:73)
    at org.hibernate.internal.SessionImpl.fireSaveOrUpdate(SessionImpl.java:682)
    at org.hibernate.internal.SessionImpl.saveOrUpdate(SessionImpl.java:674)

निष्कर्ष

mergeविधि अगर आप आशावादी लॉकिंग का उपयोग कर रहे हैं क्योंकि यह आप खो अपडेट रोकने के लिए अनुमति देता है पसंद किया जा रहा है। इस विषय के बारे में अधिक जानकारी के लिए, इस लेख को देखें

updateके रूप में यह अतिरिक्त SELECT कथन द्वारा उत्पन्न रोका जा सकता है बैच के अपडेट के लिए अच्छा है mergeआपरेशन, इसलिए बैच अद्यतन निष्पादन समय को कम करने।


अच्छा उत्तर। मैं @SelectBeforeUpdateहालांकि एनोटेशन के बारे में सोच रहा था । चयन कब चालू होता है? फोन करने पर update, फ्लश करने से ठीक पहले या यह वास्तव में कोई फर्क नहीं पड़ता है (यह बात हो सकती है अगर हाइबरनेट को फ्लश करने से पहले एक कॉल में सभी एनोटेट किए गए निकाय मिलते हैं)?
एंड्रॉनिकस

@SelectBeforeUpdateहठ प्रसंग के दौरान चयन से चलाता है flushआपरेशन। की जाँच करें में विधि अधिक जानकारी के लिए। getDatabaseSnapshotDefaultFlushEntityEventListener
व्लाद मिहालसी

10

मैं JavaDoc के लिए वापस गया org.hibernate.Sessionऔर निम्नलिखित पाया:

क्षणिक उदाहरणों को कॉल करके save(), persist()या किया जा सकता है saveOrUpdate()। लगातार उदाहरणों को कॉल करके क्षणिक बनाया जा सकता है delete()get()या load()विधि द्वारा लौटाया गया कोई भी उदाहरण लगातार है। अलग उदाहरणों को फोन करके लगातार बनाया जा सकता है update(), saveOrUpdate(), lock()या replicate()। एक क्षणिक या अलग उदाहरण की स्थिति को भी कॉल द्वारा एक नए लगातार उदाहरण के रूप में लगातार बनाया जा सकता है merge()

इस प्रकार update(), saveOrUpdate(), lock(), replicate()और merge()उम्मीदवार विकल्प हैं।

update(): यदि कोई समान पहचानकर्ता के साथ लगातार उदाहरण है, तो एक अपवाद को फेंक देगा।

saveOrUpdate(): या तो सहेजें या अपडेट करें

lock(): पदावनत

replicate(): दिए गए अलग किए गए उदाहरण की स्थिति को जारी रखें, वर्तमान पहचानकर्ता मान का पुन: उपयोग करें।

merge(): समान पहचानकर्ता के साथ एक स्थायी वस्तु लौटाता है। दिए गए उदाहरण सत्र से संबद्ध नहीं हैं।

इसलिए, lock()सीधे उपयोग नहीं किया जाना चाहिए और कार्यात्मक आवश्यकता के आधार पर उनमें से एक या अधिक को चुना जा सकता है।


7

मैंने इसे NHibernate के साथ C # में इस तरह किया था, लेकिन इसे जावा में उसी तरह काम करना चाहिए:

public virtual void Attach()
{
    if (!HibernateSessionManager.Instance.GetSession().Contains(this))
    {
        ISession session = HibernateSessionManager.Instance.GetSession();
        using (ITransaction t = session.BeginTransaction())
        {
            session.Lock(this, NHibernate.LockMode.None);
            t.Commit();
        }
    }
}

हर ऑब्जेक्ट पर पहले लॉक को कॉल किया गया था क्योंकि कॉन्टेन्स हमेशा झूठ था। समस्या यह है कि NHibernate डेटाबेस आईडी और प्रकार द्वारा वस्तुओं की तुलना करता है। इसमें equalsविधि का उपयोग किया गया है, जो संदर्भ से तुलना करता है यदि यह अधिलेखित नहीं है। उस equalsविधि के साथ यह बिना किसी अपवाद के काम करता है:

public override bool Equals(object obj)
{
    if (this == obj) { 
        return true;
    } 
    if (GetType() != obj.GetType()) {
        return false;
    }
    if (Id != ((BaseObject)obj).Id)
    {
        return false;
    }
    return true;
}

4

Session.contains(Object obj) संदर्भ की जाँच करता है और एक अलग उदाहरण का पता नहीं लगाएगा जो एक ही पंक्ति का प्रतिनिधित्व करता है और पहले से ही इसके साथ संलग्न है।

यहाँ एक पहचानकर्ता संपत्ति के साथ संस्थाओं के लिए मेरा सामान्य समाधान।

public static void update(final Session session, final Object entity)
{
    // if the given instance is in session, nothing to do
    if (session.contains(entity))
        return;

    // check if there is already a different attached instance representing the same row
    final ClassMetadata classMetadata = session.getSessionFactory().getClassMetadata(entity.getClass());
    final Serializable identifier = classMetadata.getIdentifier(entity, (SessionImplementor) session);

    final Object sessionEntity = session.load(entity.getClass(), identifier);
    // override changes, last call to update wins
    if (sessionEntity != null)
        session.evict(sessionEntity);
    session.update(entity);
}

यह .Net EntityFramework के कुछ पहलुओं में से एक है, जो मुझे पसंद है, बदले हुए संस्थाओं और उनके गुणों के बारे में अलग-अलग अनुलग्नक विकल्प।


3

मैं दृढ़ता के भंडार से एक वस्तु को "ताज़ा" करने का एक समाधान के साथ आया था जो अन्य वस्तुओं के लिए जिम्मेदार होगा जो पहले से ही सत्र से जुड़ा हो सकता है:

public void refreshDetached(T entity, Long id)
{
    // Check for any OTHER instances already attached to the session since
    // refresh will not work if there are any.
    T attached = (T) session.load(getPersistentClass(), id);
    if (attached != entity)
    {
        session.evict(attached);
        session.lock(entity, LockMode.NONE);
    }
    session.refresh(entity);
}

2

क्षमा करें, टिप्पणी (अभी तक?) जोड़ने के लिए प्रतीत नहीं हो सकता।

हाइबरनेट 3.5.0-फाइनल का उपयोग करना

जबकि Session#lockविधि इस पदावनत, जावाडोक करता उपयोग करने का सुझाव Session#buildLockRequest(LockOptions)#lock(entity)है और यदि आप सुनिश्चित करें कि आपके संघों कर cascade=lock, आलसी लोडिंग एक मुद्दा या तो नहीं है।

इसलिए, मेरा अटैचमेंट तरीका थोड़ा अच्छा लगता है

MyEntity attach(MyEntity entity) {
    if(getSession().contains(entity)) return entity;
    getSession().buildLockRequest(LockOptions.NONE).lock(entity);
    return entity;

प्रारंभिक परीक्षण से पता चलता है कि यह एक इलाज का काम करता है।


2

शायद यह एक्लिप्सिंक पर थोड़ा अलग व्यवहार करता है। बासी डेटा प्राप्त किए बिना अलग किए गए ऑब्जेक्ट को फिर से संलग्न करने के लिए, मैं आमतौर पर करता हूं:

Object obj = em.find(obj.getClass(), id);

और वैकल्पिक के रूप में एक दूसरा चरण (कैश अमान्य पाने के लिए):

em.refresh(obj)


1

मूल पद में, दो विधियाँ हैं, update(obj)और merge(obj)जिनका उल्लेख कार्य करने के लिए किया गया है, लेकिन विपरीत परिस्थितियों में। यदि यह वास्तव में सच है, तो यह देखने के लिए परीक्षण क्यों न करें कि ऑब्जेक्ट पहले से ही पहले सत्र में है, और फिर कॉल करें update(obj)यदि यह है, अन्यथा कॉल करें merge(obj)

सत्र में अस्तित्व के लिए परीक्षण है session.contains(obj)। इसलिए, मुझे लगता है कि निम्नलिखित छद्म कोड काम करेंगे:

if (session.contains(obj))
{
    session.update(obj);
}
else 
{
    session.merge(obj);
}

2
इसमें () चेक संदर्भ द्वारा तुलना करता है, लेकिन हाइबरनेट फ़ंक्शन डेटाबेस आईडी द्वारा काम करता है। session.merge को आपके कोड में कभी नहीं बुलाया जाएगा।
वीराना हन्सचमिड

1

इस ऑब्जेक्ट को रीटच करने के लिए, आपको मर्ज () का उपयोग करना होगा;

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

Example :
    Lot objAttach = em.merge(oldObjDetached);
    objAttach.setEtat(...);
    em.persist(objAttach);

0

पहले मर्ज () (लगातार उदाहरण को अद्यतन करने के लिए), फिर लॉक (LockMode.NONE) (वर्तमान उदाहरण को संलग्न करने के लिए, न कि मर्ज द्वारा लौटाए गए) () कुछ उपयोग मामलों के लिए काम करने लगता है।


0

संपत्ति hibernate.allow_refresh_detached_entityने मेरे लिए चाल चली। लेकिन यह एक सामान्य नियम है, इसलिए यह बहुत उपयुक्त नहीं है यदि आप इसे केवल कुछ मामलों में करना चाहते हैं। मुझे उम्मीद है यह मदद करेगा।

हाइबरनेट 5.4.9 पर परीक्षण किया गया

SessionFactoryOptionsBuilder



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