2009-10-28 28 views
78

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

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

+0

यह भी देखें http://stackoverflow.com/a/39827962/548473 (वसंत-डेटा-जेपीए कार्यान्वयन) – GKislin

उत्तर

55

हाइबरनेट जब/documentation

में equals()/hashCode() ओवरराइड करने के लिए कैसे का एक अच्छा और लंबे समय से वर्णन यह का सार है कि आप केवल इसके बारे में चिंता करने के लिए अगर आपके इकाई एक Set या यदि आप का हिस्सा होगा की जरूरत है अपने उदाहरणों को अलग/संलग्न करने जा रहे हैं। उत्तरार्द्ध सामान्य नहीं है। पूर्व आमतौर पर सबसे अच्छा नियंत्रित किया जाता है के माध्यम से:

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

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

+4

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

+0

समझ में आता है लेकिन कभी भी पहचान का उपयोग नहीं किया जाता है हैशकोड स्वयं हालांकि मुझे लगता है कि यह हाइबरनेट स्रोत में उपयोग किया जाता है जैसे कि उनके परिणाम ट्रान्सफॉर्मर्स –

4

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

+0

मुझे लगता है कि हमने ऐसा किया है जहां आईडी उत्पन्न नहीं हुई है, जहां आईडी उत्पन्न नहीं हुई है –

+2

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

29

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

डिफ़ॉल्ट कार्यान्वयन ज्यादातर मामलों के लिए काफी अच्छा है:

मूल सवाल का जवाब करने के लिए?

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

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

स्वीकार्य उत्तर इंगित करता है कि या तो स्थिति सत्य होने पर विधियों को ओवरराइड करने की आवश्यकता है।

+0

यह मेरे अवलोकन के साथ संरेखित है, यह जानने के लिए समय [क्यों] (http://docs.jboss.org/hibernate/core/4.0/manual/en-US/html/persistentent -classes.html # लगातार वर्गों-equalshashcode)। –

+0

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

11

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

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

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

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

जो भी एक अच्छा अभ्यास है, के रूप में Implementing equals in Java Practices पर समझाया।

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

+2

क्या स्पष्टीकरण, पृथ्वी पर जाने के लिए सर धन्यवाद .. –

+0

यह काम करता है यदि आप कंक्रीट कक्षाओं की वस्तुओं की तुलना कर रहे हैं, जो मेरी स्थिति में काम नहीं करता है। मैं सुपर क्लास की ऑब्जेक्ट्स की तुलना कर रहा था, इस मामले में यह कोड मेरे लिए काम करता था: obj1.getClass()। IsInstance (obj2) – Tad

9

सर्वोत्तम equals/hashCode कार्यान्वयन तब होता है जब आप unique business key का उपयोग करते हैं।

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

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

तुम भी equals और hashCode के लिए इकाई पहचानकर्ता का उपयोग कर सकते हैं, लेकिन यह है कि आप हमेशा एक ही hashCode मान देने के लिए इतना है कि आप यह सुनिश्चित करें कि इकाई hashCode मूल्य सभी इकाई राज्य संक्रमण भर में लगातार है की आवश्यकता है। this post for more on this topic देखें।

+0

यूयूआईडी दृष्टिकोण के लिए +1। इसे 'बेसइन्टिटी' में रखें और उस समस्या के बारे में फिर कभी सोचना न करें। यह डीबी पक्ष पर थोड़ा सा स्थान लेता है लेकिन वह कीमत जो आपको आराम के लिए बेहतर भुगतान करती है :) –

0

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

लेख से एक महत्वपूर्ण लाइन का हवाला देते हुए:

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

सरल शब्दों में

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 
} 

} 
2

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

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

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

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

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

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

1

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

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

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

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

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

संबंधित मुद्दे