JPA और हाइबरनेट का उपयोग करते समय समान और हैशकोड कैसे लागू किया जाना चाहिए


103

हाइबरनेट में मॉडल वर्ग के बराबर और हैशकोड कैसे लागू किया जाना चाहिए? आम नुकसान क्या हैं? क्या अधिकांश मामलों के लिए डिफ़ॉल्ट कार्यान्वयन पर्याप्त है? क्या व्यावसायिक कुंजी का उपयोग करने का कोई अर्थ है?

यह मुझे लगता है कि यह हर स्थिति में काम करने के लिए सही है, जब आलसी लाने, आईडी पीढ़ी, प्रॉक्सी, आदि को ध्यान में रखा जाता है।


यह भी देखें stackoverflow.com/a/39827962/548473 (वसंत-डेटा-
जपा

जवाबों:


74

हाइबरनेट में प्रलेखन में कब / कैसे ओवरराइड equals()/ करना है इसका अच्छा और लंबा वर्णन हैhashCode()

इसका सार यह है कि आपको केवल इस बारे में चिंता करने की आवश्यकता है कि क्या आपकी इकाई का हिस्सा होगा Setया यदि आप इसके उदाहरणों को अलग कर रहे हैं / संलग्न कर रहे हैं। उत्तरार्द्ध इतना सामान्य नहीं है। पूर्व आमतौर पर सबसे अच्छा होता है:

  1. आधार equals()/hashCode()एक व्यापार कुंजी पर जैसे - विशेषताओं का एक अनूठा संयोजन जो जीवनकाल में वस्तु (या, कम से कम, सत्र) के दौरान बदलने वाला नहीं है।
  2. यदि उपरोक्त नामुमकिन है, तो प्राथमिक कुंजी पर आधार equals()/ hashCode()यदि यह सेट है और वस्तु पहचान / System.identityHashCode()अन्यथा। यहां महत्वपूर्ण हिस्सा यह है कि नई इकाई को इसमें शामिल करने और कायम रखने के बाद आपको अपना सेट पुनः लोड करने की आवश्यकता है ; अन्यथा आप अजीब व्यवहार (अंततः त्रुटियों और / या डेटा भ्रष्टाचार के परिणामस्वरूप) के साथ समाप्त हो सकते हैं क्योंकि आपकी इकाई को एक बाल्टी से आवंटित किया जा सकता है जो उसके वर्तमान से मेल नहीं खाता है hashCode()

1
जब आप कहते हैं "पुनः लोड करें" @ ChssPly76 आपका मतलब क्या है refresh()? आपकी इकाई कैसे काम करती है, जो Setगलत बाल्टी में अनुबंध समाप्त करने का पालन करता है (यह मानते हुए कि आपके पास एक अच्छा पर्याप्त हैशकोड कार्यान्वयन है)।
गैर-अनुक्रमिक

4
संग्रह को ताज़ा करें या संपूर्ण (स्वामी) इकाई को पुनः लोड करें, हाँ। जहां तक ​​गलत बाल्टी जाती है: ए) आप सेट करने के लिए नई इकाई जोड़ते हैं, इसकी आईडी अभी तक सेट नहीं है, इसलिए आप पहचान का उपयोग कर रहे हैंहैशकोड जो आपकी इकाई को बाल्टी # 1 में रखता है। बी) आपकी इकाई (सेट के भीतर) कायम है, अब इसमें एक आईडी है और इस प्रकार आप उस आईडी के आधार पर हैशकोड () का उपयोग कर रहे हैं। यह ऊपर से अलग है और आपकी इकाई को बाल्टी # 2 में रखा होगा । अब, यह मानते हुए कि आप इस इकाई का संदर्भ कहीं और रखते हैं, कॉल करने का प्रयास करें Set.contains(entity)और आप वापस आ जाएंगे false। उसी के लिए जाता है () / डाल () / आदि ...
ChssPly76

समझ में आता है, लेकिन कभी भी पहचान का इस्तेमाल नहीं किया। हालांकि मैं इसे अपने रिजल्ट ट्रांसफॉर्मर की तरह हाइबरनेट स्रोत में इस्तेमाल करता देख रहा हूं
नॉन सीक्वेंटर

1
हाइबरनेट का उपयोग करते समय, आप इस समस्या में भी भाग सकते हैं , जिसका समाधान मुझे अभी भी नहीं मिला है।
जियोवन्नी बोट्टा

@ ChssPly76 व्यावसायिक नियमों के कारण जो यह निर्धारित करते हैं कि दो वस्तुएं समान हैं, मुझे उन गुणों पर अपने बराबर / हैशकोड विधियों को आधार बनाने की आवश्यकता होगी जो किसी वस्तु के जीवनकाल में बदल सकते हैं। क्या यह वास्तव में एक बड़ी बात है? यदि ऐसा है तो मैं इसके आसपास कैसे पहुंचूं?
ubiquibacon

39

मुझे नहीं लगता कि स्वीकृत उत्तर सटीक है।

मूल प्रश्न का उत्तर देने के लिए:

क्या अधिकांश मामलों के लिए डिफ़ॉल्ट कार्यान्वयन पर्याप्त है?

इसका उत्तर हां में है, ज्यादातर मामलों में यह है।

आपको केवल ओवरराइड करने की आवश्यकता है equals()और hashcode()यदि इकाई का उपयोग एक Set(जो बहुत सामान्य है) और में किया जाएगा इकाई को अलग कर दिया जाएगा, और बाद में फिर से संलग्न किया जाएगा, हाइबरनेट सत्र (जो हाइबरनेट का एक असामान्य उपयोग है)।

स्वीकार किए गए उत्तर से संकेत मिलता है कि यदि स्थिति सही है, तो तरीकों को ओवरराइड करने की आवश्यकता है।


यह मेरे अवलोकन के साथ संरेखित करता है, यह पता लगाने का समय कि क्यों
विल्स्टिमिल ओवेरिक्स्क

"आपको केवल समतुल्य () और हैशकोड को ओवरराइड करने की आवश्यकता है (यदि सेट में इकाई का उपयोग किया जाएगा" पूरी तरह से पर्याप्त है यदि कुछ फ़ील्ड किसी ऑब्जेक्ट की पहचान करते हैं, और इसलिए आप ऑब्जेक्ट को पहचानने के लिए Object.equals () पर भरोसा नहीं करना चाहते हैं। वस्तुओं।
davidxxx

17

सबसे अच्छा equals/ hashCodeकार्यान्वयन तब होता है जब आप एक अद्वितीय व्यवसाय कुंजी का उपयोग करते हैं

व्यापार कुंजी सभी इकाई राज्य परिवर्तनों के अनुरूप होनी चाहिए (क्षणिक, संलग्न, अलग, हटाए गए) के , यही कारण है कि आप समानता के लिए आईडी पर भरोसा नहीं कर सकते।

एक अन्य विकल्प यूयूआईडी पहचानकर्ताओं का उपयोग करने के लिए स्विच करना है , जिसे एप्लिकेशन लॉजिक द्वारा सौंपा गया है। इस तरह, आप equals/ के लिए UUID का उपयोग कर सकते हैंhashCode क्योंकि आईडी को निकाले जाने से पहले असाइन किया गया है।

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


Uuid दृष्टिकोण के लिए +1। उस में डाल दिया BaseEntityऔर उस समस्या के बारे में फिर कभी नहीं सोचते। यह डीबी की तरफ थोड़ा सा स्थान लेता है, लेकिन यह कीमत आपको आराम के लिए बेहतर भुगतान करती है :)
मार्टिन फ्री

12

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

if (getClass() != that.getClass()) return false;

इसके बजाय उपयोग करें:

if (!(otherObject instanceof Unit)) return false;

जो कि एक अच्छा अभ्यास भी है, जैसा कि जावा प्रैक्टिस में बराबर लागू होता है

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


1
यह काम करता है यदि आप ठोस वर्गों की वस्तुओं की तुलना कर रहे हैं, जो मेरी स्थिति में काम नहीं करते थे। मैं सुपर क्लास की वस्तुओं की तुलना कर रहा था, जिस स्थिति में यह कोड मेरे लिए काम करता था: obj1.getClass ()) है। Instance (obj2)
Tad

6

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


मुझे लगता है कि हमने उस मामले में वस्तु पहचान का उपयोग करने के लिए किया है जहां आईडी उत्पन्न नहीं हुई है
कैथी वान स्टोन

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

1

हाइबरनेट 5.2 के दस्तावेज़ीकरण में यह कहा गया है कि आप अपनी स्थिति के आधार पर हैशकोड और समान लागू नहीं करना चाहते हैं।

https://docs.jboss.org/hibernate/orm/5.2/userguide/html_single/Hibernate_User_Guide.html#mapping-model-pojo-equalshashcode

आम तौर पर, एक ही सत्र से भरी हुई दो वस्तुएं समान होंगी यदि वे डेटाबेस में समान हैं (हैशकोड को लागू करने और बराबर करने के बिना)।

यदि आप दो या अधिक सत्रों का उपयोग कर रहे हैं तो यह जटिल हो जाता है। इस मामले में, दो वस्तुओं की समानता आपके बराबर-विधि कार्यान्वयन पर निर्भर करती है।

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


0

यहाँ बहुत अच्छा लेख है: https://docs.jboss.org/hibernate/stable/core.old/reference/en/html/persistent-classes-equalshashcode.html

लेख से एक महत्वपूर्ण पंक्ति उद्धृत करते हुए:

हम व्यावसायिक कुंजी समानता का उपयोग करके समान () और हैशकोड () को लागू करने की सलाह देते हैं। व्यापार कुंजी समानता का मतलब है कि बराबरी () विधि केवल उन गुणों की तुलना करती है जो व्यापार कुंजी बनाते हैं, एक कुंजी जो वास्तविक दुनिया में हमारे उदाहरण की पहचान करेगी (एक प्राकृतिक उम्मीदवार कुंजी):

समान्य शब्दों में

public class Cat {

...
public boolean equals(Object other) {
    //Basic test / class cast
    return this.catId==other.catId;
}

public int hashCode() {
    int result;

    return 3*this.catId; //any primenumber 
}

}

0

यदि आप ओवरराइड करने के लिए हुए हैं equals, तो सुनिश्चित करें कि आप इसके अनुबंधों को पूरा करते हैं: -

  • समरूपता
  • चिंतनशील
  • सकर्मक
  • लगातार
  • NON NULL

और ओवरराइड hashCode, जैसा कि इसका अनुबंध equalsकार्यान्वयन पर निर्भर करता है।

जोशुआ बलोच (संग्रह ढांचे के डिजाइनर) ने इन नियमों का दृढ़ता से पालन करने का आग्रह किया।

  • आइटम 9: जब आप ओवरराइड करते हैं तो हमेशा हैशकोड को ओवरराइड करें

जब आप इन अनुबंधों का पालन नहीं करते हैं तो गंभीर अनपेक्षित प्रभाव होते हैं। उदाहरण के लिए List#contains(Object o)गलत booleanमूल्य वापस आ सकता है क्योंकि सामान्य अनुबंध पूरा नहीं हुआ है।

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