दिलचस्प पोस्ट
मैं आस-पास के स्थानों जैसे कि बैंक, रेस्तरां, एटीएम, गूगल एरिया में गूगल मैप्स के क्षेत्र में खोज करने की कोशिश कर रहा हूं ध्वज के साथ अजगर re.sub सभी घटनाओं को प्रतिस्थापित नहीं करता है क्या सभी उपलब्ध नेटवर्क प्रिंटर की गणना करने के लिए कोई .NET तरीका है? कुछ असिंक्रोनस कार्यों को पूरा करने का सरल तरीका, जावास्क्रिप्ट में? हैश किस प्रकार का वर्डप्रेस इस्तेमाल करता है? जावास्क्रिप्ट MySQL के साथ कनेक्ट कर सकते हैं? एंड्रॉइड / जावा – फेसबुक वॉल को सरल पाठ पोस्ट करें? मैं अजगर में एक सूची को कैसे उलटा सकता हूं? नाकआउट के बीच अंतर ऑब्जेक्ट literals बनाम फ़ंक्शन के रूप में घोषित मॉडल देखें Three.js: रोटेट ऑब्जेक्ट के बच्चों को जोड़ना और निकालना कंडीशनल वेरिएबल बनाम सेमाफोर कैसे JQuery क्लोन () और आईडी बदलने के लिए 'सुपर' कीवर्ड के साथ जेनरिक बाउंडिंग Angular2 QuickStart npm प्रारंभ ठीक से काम नहीं कर रहा है एकल थ्रेड के लिए नींद () का उपयोग करना

जेपीए हैशोड () / बराबर () दुविधा

जेपीए संस्थाओं के बारे में यहां कुछ चर्चाएं हुई हैं और जेपीए इकाई वर्ग के लिए hashCode() / equals() कार्यान्वयन का उपयोग किया जाना चाहिए उनमें से अधिकांश (अगर सभी नहीं) सीतनिद्रा में होना पर निर्भर हैं, लेकिन मैं उनके बारे में JPA- कार्यान्वयन- neutrally चर्चा करना चाहते हैं (मैं ईक्लिप्स लिंक का उपयोग कर रहा हूँ)।

सभी संभव कार्यान्वयन के बारे में अपने फायदे और नुकसान हो रहे हैं:

  • List / Set ऑपरेशन के लिए hashCode() / equals() अनुबंध अनुरूपता (अपरिवर्तनीयता)
  • चाहे समान वस्तुएं (जैसे कि विभिन्न सत्रों से, सुस्त-लोड किए गए डेटा संरचनाओं से गतिशील प्रॉक्सी) पता लगाया जा सकता है
  • चाहे संस्था अलग-अलग (या गैर-निरंतर) स्थिति में सही ढंग से व्यवहार करती है

जहाँ तक मैं देख सकता हूं, तीन विकल्प हैं :

  1. ओवरराइड न करें; Object.equals() और Object.hashCode() पर भरोसा करें
    • hashCode() / equals() काम
    • समान वस्तुओं की पहचान नहीं कर सकते हैं, गतिशील प्रॉक्सी के साथ समस्याएं
    • अलग संस्थाओं के साथ कोई समस्या नहीं
  2. प्राथमिक कुंजी के आधार पर उन्हें ओवरराइड करें
    • hashCode() / equals() टूटा हुआ है
    • सही पहचान (सभी प्रबंधित संस्थाओं के लिए)
    • अलग संस्थाओं के साथ समस्याएं
  3. बिजनेस-आईडी (गैर-प्राथमिक कुंजी क्षेत्रों के आधार पर, विदेशी कुंजी के बारे में क्या?) के आधार पर उन्हें ओवरराइड करें
    • hashCode() / equals() टूटा हुआ है
    • सही पहचान (सभी प्रबंधित संस्थाओं के लिए)
    • अलग संस्थाओं के साथ कोई समस्या नहीं

मेरे प्रश्न हैं:

  1. क्या मुझे एक विकल्प और / या समर्थक / चुनाव बिंदु याद आये?
  2. आपने क्या विकल्प चुना और क्यों?

अद्यतन 1:

" hashCode() / equals() टूटी" से, मेरा मतलब है कि लगातार hashCode() इनवॉशन्स अलग-अलग मूल्यों को वापस कर सकते हैं, जो (जब सही ढंग से कार्यान्वित) Object एपीआई दस्तावेज के अर्थ में टूटा हुआ नहीं है, लेकिन यह कोशिश करते समय समस्याओं का कारण बनता है किसी Map , Set या अन्य हैश-आधारित Collection से परिवर्तित इकाई को पुनः प्राप्त करने के लिए। नतीजतन, जेपीए के कार्यान्वयन (कम से कम ईक्लिप्स लिंक) कुछ मामलों में ठीक से काम नहीं करेगा।

अद्यतन 2:

आपके उत्तर के लिए धन्यवाद – इनमें से अधिकतर उल्लेखनीय गुणवत्ता हैं
दुर्भाग्य से, मैं अभी भी अनिश्चित हूँ कि वास्तविक जीवन आवेदन के लिए कौन से दृष्टिकोण सबसे अच्छा होगा, या मेरे आवेदन के लिए सबसे अच्छा तरीका कैसे तय किया जाए। तो, मैं सवाल खुलता हूं और कुछ और चर्चाओं और / या रायओं के लिए आशा रखता हूं।

वेब के समाधान से एकत्रित समाधान "जेपीए हैशोड () / बराबर () दुविधा"

इस विषय पर यह बहुत अच्छा लेख पढ़ें: सीतनिद्रा में होना नहीं आपकी पहचान चोरी करना

लेख का निष्कर्ष इस तरह चला जाता है:

ऑब्जेक्ट पहचान सही तरीके से कार्यान्वित करने के लिए बेहद मुश्किल है जब ऑब्जेक्ट्स को डेटाबेस में कायम रखा जाता है हालांकि, समस्याओं को पूरी तरह से स्टेम नहीं किया जाता है क्योंकि ऑब्जेक्ट को बिना किसी आईडी के अस्तित्व में रखने से पहले वे सहेजे जाते हैं। ऑब्जेक्ट आईडी को ऑब्जेक्ट-रिलेशनल मैपिंग फ्रेमवर्क जैसे कि सीतनिद्रा में होना निर्दिष्ट करने की ज़िम्मेदारी लेकर हम इन समस्याओं को हल कर सकते हैं। इसके बजाय, ऑब्जेक्ट आईडी को तत्काल असाइन किया जा सकता है जैसे ऑब्जेक्ट को तत्काल दिया जाता है। यह वस्तु पहचान सरल और त्रुटि-मुक्त बनाता है, और डोमेन मॉडल में आवश्यक कोड की मात्रा कम कर देता है।

मैं हमेशा ओवरराइड बराबर / हैशकोड और व्यवसाय आईडी के आधार पर इसे लागू करता हूं मेरे लिए सबसे उचित समाधान लगता है निम्नलिखित लिंक देखें

इस सब चीजों को शामिल करने के लिए, यहां एक सूची है कि क्या काम करेगा या बराबर / हैशोड को संभालने के विभिन्न तरीकों से काम नहीं करेगा: यहां छवि विवरण दर्ज करें

संपादित करें :

यह समझाने के लिए कि यह मेरे लिए क्यों काम करता है:

  1. मैं आमतौर पर अपने जेपीए आवेदन में हैश-आधारित संग्रह (हैशमैप / हैशसेट) का उपयोग नहीं करता हूं। अगर मुझे चाहिए तो, मैं अनन्य लिस्ट समाधान बनाना चाहता हूं।
  2. मुझे लगता है कि रनटाइम पर बिजनेस आईडी बदलना किसी भी डेटाबेस अनुप्रयोग के लिए सर्वोत्तम अभ्यास नहीं है दुर्लभ मामलों में जहां कोई अन्य समाधान नहीं है, मैं विशेष उपचार करना चाहता हूं जैसे तत्व को निकालना और इसे हैश-आधारित संग्रह में वापस डाल दिया।
  3. मेरे मॉडल के लिए, मैं कन्स्ट्रक्टर पर व्यवसाय आईडी सेट करता हूं और उसके लिए सेटर्स प्रदान नहीं करता। मैं संपत्ति के बजाय क्षेत्र को बदलने के लिए जेपीए लागू करने देता हूं।
  4. यूयूआईडी समाधान अतिरेक लगता है क्यों यूयूआईडी अगर आपके पास प्राकृतिक व्यवसाय आईडी है? मैं डेटाबेस में व्यवसाय आईडी के विशिष्टता को स्थापित करने के बाद। डेटाबेस में प्रत्येक तालिका के लिए तीन अनुक्रमित होने क्यों?

आम तौर पर हमारी संस्थाओं में दो आईडी हैं:

  1. केवल दृढ़ता परत के लिए है (इसलिए कि दृढ़ता प्रदाता और डेटाबेस वस्तुओं के बीच संबंधों को समझ सकता है)
  2. हमारे अनुप्रयोग की आवश्यकताओं के लिए ( equals() और hashCode() विशेष रूप से)

जरा देखो तो:

 @Entity public class User { @Id private int id; // Persistence ID private UUID uuid; // Business ID // assuming all fields are subject to change // If we forbid users change their email or screenName we can use these // fields for business ID instead, but generally that's not the case private String screenName; private String email; // I don't put UUID generation in constructor for performance reasons. // I call setUuid() when I create a new entity public User() { } // This method is only called when a brand new entity is added to // persistence context - I add it as a safety net only but it might work // for you. In some cases (say, when I add this entity to some set before // calling em.persist()) setting a UUID might be too late. If I get a log // output it means that I forgot to call setUuid() somewhere. @PrePersist public void ensureUuid() { if (getUuid() == null) { log.warn(format("User's UUID wasn't set on time. " + "uuid: %s, name: %s, email: %s", getUuid(), getScreenName(), getEmail())); setUuid(UUID.randomUUID()); } } // equals() and hashCode() rely on non-changing data only. Thus we // guarantee that no matter how field values are changed we won't // lose our entity in hash-based Sets. @Override public int hashCode() { return getUuid().hashCode(); } // Note that I don't use direct field access inside my entity classes and // call getters instead. That's because Persistence provider (PP) might // want to load entity data lazily. And I don't use // this.getClass() == other.getClass() // for the same reason. In order to support laziness PP might need to wrap // my entity object in some kind of proxy, ie subclassing it. @Override public boolean equals(final Object obj) { if (this == obj) return true; if (!(obj instanceof User)) return false; return getUuid().equals(((User) obj).getUuid()); } // Getters and setters follow } 

संपादित करें: setUuid() विधि को setUuid() करने के लिए कॉल के बारे में अपने बिंदु को स्पष्ट करने के लिए यहां एक विशिष्ट परिदृश्य है:

 User user = new User(); // user.setUuid(UUID.randomUUID()); // I should have called it here user.setName("Master Yoda"); user.setEmail("yoda@jedicouncil.org"); jediSet.add(user); // here's bug - we forgot to set UUID and //we won't find Yoda in Jedi set em.persist(user); // ensureUuid() was called and printed the log for me. jediCouncilSet.add(user); // Ok, we got a UUID now 

जब मैं अपना परीक्षण चलाता हूं और लॉग आउटपुट को देखता हूं तो मैं समस्या को ठीक करता हूं:

 User user = new User(); user.setUuid(UUID.randomUUID()); 

वैकल्पिक रूप से, कोई एक अलग निर्माता प्रदान कर सकता है:

 @Entity public class User { @Id private int id; // Persistence ID private UUID uuid; // Business ID ... // fields // Constructor for Persistence provider to use public User() { } // Constructor I use when creating new entities public User(UUID uuid) { setUuid(uuid); } ... // rest of the entity. } 

तो मेरा उदाहरण ऐसा दिखेगा:

 User user = new User(UUID.randomUUID()); ... jediSet.add(user); // no bug this time em.persist(user); // and no log output 

मैं एक डिफॉल्ट कन्स्ट्रक्टर और एक सेटर का उपयोग करता हूं, लेकिन आप दो कन्स्ट्रक्टरों को आपके लिए और अधिक उपयुक्त समझ सकते हैं।

यदि आप अपने समूह के लिए equals()/hashCode() का उपयोग करना चाहते हैं, तो यह अर्थ है कि एक ही इकाई केवल एक बार में हो सकती है, तो केवल एक ही विकल्प है: विकल्प 2. ऐसा इसलिए है क्योंकि परिभाषा के द्वारा किसी संस्था के लिए प्राथमिक कुंजी कभी नहीं बदलता है (अगर कोई वास्तव में इसे अपडेट करता है, तो यह वही संस्था नहीं है)

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

अब, यह सैद्धांतिक रूप से भी विकल्प 3 के साथ हासिल करना संभव है, भले ही तथाकथित "व्यवसाय-चाबियाँ" में वह खराब बदलाव होता है, जो कि वे बदल सकते हैं: "आपको बस इतना करना होगा कि वह सेट से पहले से सम्मिलित संस्थाओं को हटा दें ( s), और उन्हें पुनः डालें। " यह सच है – लेकिन यह भी इसका मतलब है कि, वितरित प्रणाली में, आपको यह सुनिश्चित करना होगा, यह पूरी तरह से हर जगह किया जाता है जहां पर डेटा डाला गया है (और आपको यह सुनिश्चित करना होगा, कि अपडेट किया जाता है , इससे पहले कि अन्य चीजें होती हैं)। आपको एक अत्याधुनिक अद्यतन तंत्र की आवश्यकता होगी, खासकर यदि कुछ दूरस्थ सिस्टम वर्तमान में पहुंच योग्य नहीं हैं …

विकल्प 1 का उपयोग केवल तभी किया जा सकता है, यदि आपके सेट्स में सभी ऑब्जेक्ट समान हाइबरनेट सत्र से हैं। सीतनिद्रा में होना प्रलेखन अध्याय 13.1.3 में यह बहुत स्पष्ट बनाता है । वस्तु पहचान को ध्यान में रखते हुए :

एक सत्र के भीतर, ऑब्जेक्ट की तुलना करने के लिए आवेदन सुरक्षित रूप से == का उपयोग कर सकते हैं।

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

यह विकल्प 3 के पक्ष में बहस जारी है:

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

यह सच है, यदि आप

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

अन्यथा, आप विकल्प 2 चुनने के लिए स्वतंत्र हैं

फिर यह एक रिश्तेदार स्थिरता की आवश्यकता का उल्लेख करता है:

व्यावसायिक कुंजी के लिए गुण डेटाबेस प्राथमिक कुंजी के रूप में स्थिर होने की आवश्यकता नहीं है; आपको केवल स्थिरता की गारंटी है जब तक वस्तुएं एक ही सेट में होती हैं।

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


लघु संस्करण:

  • ऑप्शन 1 का प्रयोग केवल एक ही सत्र में ही ऑब्जेक्ट के साथ किया जा सकता है।
  • यदि आप कर सकते हैं, तो विकल्प 2 का प्रयोग करें। (जितनी जल्दी हो सके पीके निरुपित करें, क्योंकि आप पीके को असाइन किए जाने तक सेट में वस्तुओं का उपयोग नहीं कर सकते।)
  • यदि आप रिश्तेदार स्थिरता की गारंटी दे सकते हैं, तो आप विकल्प 3 का उपयोग कर सकते हैं। लेकिन इस से सावधानी बरतें।

मैंने व्यक्तिगत रूप से विभिन्न परियोजनाओं में इन सभी तीन चरणों का इस्तेमाल किया है मुझे कहना होगा कि विकल्प 1 मेरी राय में है कि वास्तविक जीवन एप में सबसे अधिक व्यावहारिक है। ए ने अनुभव किया हैश कोड को तोड़ दिया () / बराबर () अनुरूपता कई पागल बग की ओर जाता है, क्योंकि हर बार ऐसी परिस्थितियों में समाप्त हो जाता है, जहां एक इकाई के बाद समानता में परिवर्तन का संग्रह संग्रह में जोड़ा गया है।

लेकिन आगे के विकल्प हैं (अपने पेशेवरों और विपक्षों के साथ भी):


ए) हैशोड / अस्थिर के एक सेट के आधार पर बराबर है, नल नहीं , कंसट्रक्टर को सौंपा , फ़ील्ड

(+) सभी तीन मानदंडों की गारंटी है

(-) फील्ड मान एक नया उदाहरण बनाने के लिए उपलब्ध होना चाहिए

(-) अगर आप में से एक को बदलना होगा तो हेन्डलिंग को जटिल करना


बी) हैशोड / प्राथमिक कुंजी पर आधारित बराबर है जो कि जेपीए के बजाय आवेदन में (कन्स्ट्रक्टर में) सौंपा गया है

(+) सभी तीन मानदंडों की गारंटी है

(-) आप डीबी सीक्वैस जैसी साधारण विश्वसनीय आईडी पीढ़ी की रणनीति का लाभ नहीं उठा सकते हैं

(-) जटिल अगर नई इकाइयां वितरित वातावरण (क्लाइंट / सर्वर) या एप सर्वर क्लस्टर में बनाई गई हैं


सी) इकाई के निर्माता द्वारा निर्दिष्ट यूयूआईडी के आधार पर हैशोड / बराबर है

(+) सभी तीन मानदंडों की गारंटी है

(-) UUID पीढ़ी के ऊपर

(-) एक छोटे से जोखिम हो सकता है जो एक ही यूयूआईडी से दो बार उपयोग किया जाता है, अल्गोरिथ के आधार पर (डीबी पर एक अनूठी इंडेक्स द्वारा पता लगाया जा सकता है)

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

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

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

विकल्प 1 के साथ सबसे बड़ी समस्या यह है कि पृथक उदाहरणों की तुलना निरंतर उदाहरणों के साथ नहीं की जा सकती है। किन्तु वह ठीक है; बराबर और हैशडोड का अनुबंध डेवलपर को छोड़ता है यह तय करने के लिए कि प्रत्येक वर्ग के लिए समानता का क्या अर्थ है। तो बस बराबर और हैशोडोड ऑब्जेक्ट से बीते हैं। अगर आपको एक निरंतर उदाहरण के साथ एक अलग उदाहरण की तुलना करने की आवश्यकता है, तो आप उस उद्देश्य के लिए स्पष्ट रूप से एक नई पद्धति बना सकते हैं, शायद boolean sameEntity या boolean dbEquivalent या boolean businessEquals boolean dbEquivalent

  1. यदि आपके पास एक व्यवसाय कुंजी है , तो आपको उस के equals / hashCode लिए उपयोग करना चाहिए
  2. यदि आपके पास व्यवसाय की कुंजी नहीं है, तो आपको इसे डिफ़ॉल्ट Object बराबर और हैश कोड कार्यान्वयन से नहीं छोड़ना चाहिए क्योंकि वह merge और इकाई के बाद काम नहीं करता है।
  3. आप इस पोस्ट में सुझाव दिए गए अनुसार इकाई पहचानकर्ता का उपयोग कर सकते हैं । एकमात्र पकड़ यह है कि आपको hashCode कार्यान्वयन का उपयोग करने की आवश्यकता होती है जो हमेशा समान मूल्य देता है, जैसे:

     @Entity public class Book implements Identifiable<Long> { @Id @GeneratedValue private Long id; private String title; @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof Book)) return false; Book book = (Book) o; return getId() != null && Objects.equals(getId(), book.getId()); } @Override public int hashCode() { return 31; } //Getters and setters omitted for brevity } 

मैं एंड्रयू के उत्तर से सहमत हूँ हम अपने आवेदन में एक ही काम करते हैं, लेकिन यूएआईडी को वीएआरएआर / सीआरएआर के रूप में संग्रहित करने के बजाय, हम इसे दो लंबे मानों में विभाजित करते हैं। देखें UUID.getLeastSignificantBits () और UUID.getMostSignificantBits ()।

एक और बात पर विचार करने के लिए, यह है कि UUID.randomUUID () को कॉल बहुत धीमी है, इसलिए आप जब जरूरत पड़ने पर केवल UUID को उत्पन्न करने की कोशिश कर सकते हैं, जैसे कि दृढ़ता या कॉल के बराबर () / हैशोड ()

 @MappedSuperclass public abstract class AbstractJpaEntity extends AbstractMutable implements Identifiable, Modifiable { private static final long serialVersionUID = 1L; @Version @Column(name = "version", nullable = false) private int version = 0; @Column(name = "uuid_least_sig_bits") private long uuidLeastSigBits = 0; @Column(name = "uuid_most_sig_bits") private long uuidMostSigBits = 0; private transient int hashCode = 0; public AbstractJpaEntity() { // } public abstract Integer getId(); public abstract void setId(final Integer id); public boolean isPersisted() { return getId() != null; } public int getVersion() { return version; } //calling UUID.randomUUID() is pretty expensive, //so this is to lazily initialize uuid bits. private void initUUID() { final UUID uuid = UUID.randomUUID(); uuidLeastSigBits = uuid.getLeastSignificantBits(); uuidMostSigBits = uuid.getMostSignificantBits(); } public long getUuidLeastSigBits() { //its safe to assume uuidMostSigBits of a valid UUID is never zero if (uuidMostSigBits == 0) { initUUID(); } return uuidLeastSigBits; } public long getUuidMostSigBits() { //its safe to assume uuidMostSigBits of a valid UUID is never zero if (uuidMostSigBits == 0) { initUUID(); } return uuidMostSigBits; } public UUID getUuid() { return new UUID(getUuidMostSigBits(), getUuidLeastSigBits()); } @Override public int hashCode() { if (hashCode == 0) { hashCode = (int) (getUuidMostSigBits() >> 32 ^ getUuidMostSigBits() ^ getUuidLeastSigBits() >> 32 ^ getUuidLeastSigBits()); } return hashCode; } @Override public boolean equals(final Object obj) { if (obj == null) { return false; } if (!(obj instanceof AbstractJpaEntity)) { return false; } //UUID guarantees a pretty good uniqueness factor across distributed systems, so we can safely //dismiss getClass().equals(obj.getClass()) here since the chance of two different objects (even //if they have different types) having the same UUID is astronomical final AbstractJpaEntity entity = (AbstractJpaEntity) obj; return getUuidMostSigBits() == entity.getUuidMostSigBits() && getUuidLeastSigBits() == entity.getUuidLeastSigBits(); } @PrePersist public void prePersist() { // make sure the uuid is set before persisting getUuidLeastSigBits(); } } 

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

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

हालांकि सावधानी के शब्द: अगर आपकी संस्थाएं अलग-अलग तालिकाओं में जमा हो जाती हैं और आपके प्रदाता प्राथमिक कुंजी के लिए एक ऑटो-पीढ़ी की रणनीति का उपयोग करते हैं, तो आपको संपूर्ण प्रकार की प्राथमिक कुंजी को डुप्लिकेट मिलेगा। इस तरह के मामले में, ऑब्जेक्ट # getClass () को कॉल के साथ रन टाइम प्रकारों की भी तुलना करें, जो निश्चित रूप से इसे दो अलग-अलग प्रकार के बराबर मानते हैं। यह मुझे सबसे अधिक भाग के लिए ठीक ठीक है।

जाहिर है पहले से ही यहां बहुत जानकारीपूर्ण उत्तर हैं लेकिन मैं आपको बताऊंगा कि हम क्या करते हैं।

हम कुछ नहीं करते हैं (यानी ओवरराइड न करें)

अगर हमें संग्रह के लिए काम करने के लिए / हश कोड की आवश्यकता होती है तो हम यूयूआईडी का उपयोग करते हैं। आप कन्स्ट्रक्टर में यूयूआईडी बनाते हैं। हम UWID के लिए http://wiki.fasterxml.com/JugHome का उपयोग करते हैं यूयूआईडी थोड़ा और महंगा सीपीयू बुद्धिमान है, लेकिन सीरियलाइज़ेशन और डीबी एक्सेस की तुलना में सस्ता है।

व्यावसायिक कुंजी दृष्टिकोण हमारे लिए उपयुक्त नहीं है हम डीबी उत्पन्न आईडी का उपयोग करते हैं, अस्थायी क्षणिक tempId और ओवरराइड समान () / हैश कोड () दुविधा को हल करने के लिए। सभी संस्थाएं संस्था के वंश हैं पेशेवरों:

  1. डीबी में कोई अतिरिक्त फ़ील्ड नहीं
  2. सन्तानों की संस्थाओं में कोई अतिरिक्त कोडन नहीं, सभी के लिए एक दृष्टिकोण
  3. कोई प्रदर्शन समस्याएं (जैसे यूयूआईडी के साथ), डीबी आईडी पीढ़ी
  4. हाशमैप के साथ कोई समस्या नहीं है (बराबर और आदि का उपयोग ध्यान में रखने की आवश्यकता नहीं है)
  5. जारी रखने के बाद भी नई इकाई का हशकोड समय में परिवर्तित नहीं होता है

विपक्ष:

  1. धारावाहिकों और निरंतर संस्थाओं को निरंतर बनाने में समस्याएं हो सकती हैं
  2. डीबी से पुनः लोड करने के बाद सहेजे गए इकाई का हशकोड बदल सकता है
  3. हमेशा अलग-अलग विचार किए गए ऑब्जेक्ट नहीं बने (शायद यह सही है?)
  4. और क्या?

हमारे कोड को देखें:

 @MappedSuperclass abstract public class Entity implements Serializable { @Id @GeneratedValue @Column(nullable = false, updatable = false) protected Long id; @Transient private Long tempId; public void setId(Long id) { this.id = id; } public Long getId() { return id; } private void setTempId(Long tempId) { this.tempId = tempId; } // Fix Id on first call from equal() or hashCode() private Long getTempId() { if (tempId == null) // if we have id already, use it, else use 0 setTempId(getId() == null ? 0 : getId()); return tempId; } @Override public boolean equals(Object obj) { if (super.equals(obj)) return true; // take proxied object into account if (obj == null || !Hibernate.getClass(obj).equals(this.getClass())) return false; Entity o = (Entity) obj; return getTempId() != 0 && o.getTempId() != 0 && getTempId().equals(o.getTempId()); } // hash doesn't change in time @Override public int hashCode() { return getTempId() == 0 ? super.hashCode() : getTempId().hashCode(); } } 

I have always used option 1 in the past because I was aware of these discussions and thought it was better to do nothing until I knew the right thing to do. Those systems are all still running successfully.

However, next time I may try option 2 – using the database generated Id.

Hashcode and equals will throw IllegalStateException if the id is not set.

This will prevent subtle errors involving unsaved entities from appearing unexpectedly.

What do people think of this approach?

This is a common problem in every IT system that uses Java and JPA. The pain point extends beyond implementing equals() and hashCode(), it affects how an organization refer to an entity and how its clients refer to the same entity. I've seen enough pain of not having a business key to the point that I wrote my own blog to express my view.

In short: use a short, human readable, sequential ID with meaningful prefixes as business key that's generated without any dependency on any storage other than RAM. Twitter's Snowflake is a very good example.

If UUID is the answer for many people, why don't we just use factory methods from business layer to create the entities and assign primary key at creation time?

उदाहरण के लिए:

 @ManagedBean public class MyCarFacade { public Car createCar(){ Car car = new Car(); em.persist(car); return car; } } 

this way we would get a default primary key for the entity from the persistence provider, and our hashCode() and equals() functions could rely on that.

We could also declare the Car's constructors protected and then use reflection in our business method to access them. This way developers would not be intent on instantiate Car with new, but through factory method.

How'bout that?

I tried to answer this question myself and was never totally happy with found solutions until i read this post and especially DREW one. I liked the way he lazy created UUID and optimally stored it.

But I wanted to add even more flexibility, ie lazy create UUID ONLY when hashCode()/equals() is accessed before first persistence of the entity with each solution's advantages :

  • equals() means "object refers to the same logical entity"
  • use database ID as much as possible because why would I do the work twice (performance concern)
  • prevent problem while accessing hashCode()/equals() on not yet persisted entity and keep the same behaviour after it is indeed persisted

I would really apreciate feedback on my mixed-solution below

 public class MyEntity { @Id() @Column(name = "ID", length = 20, nullable = false, unique = true) @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id = null; @Transient private UUID uuid = null; @Column(name = "UUID_MOST", nullable = true, unique = false, updatable = false) private Long uuidMostSignificantBits = null; @Column(name = "UUID_LEAST", nullable = true, unique = false, updatable = false) private Long uuidLeastSignificantBits = null; @Override public final int hashCode() { return this.getUuid().hashCode(); } @Override public final boolean equals(Object toBeCompared) { if(this == toBeCompared) { return true; } if(toBeCompared == null) { return false; } if(!this.getClass().isInstance(toBeCompared)) { return false; } return this.getUuid().equals(((MyEntity)toBeCompared).getUuid()); } public final UUID getUuid() { // UUID already accessed on this physical object if(this.uuid != null) { return this.uuid; } // UUID one day generated on this entity before it was persisted if(this.uuidMostSignificantBits != null) { this.uuid = new UUID(this.uuidMostSignificantBits, this.uuidLeastSignificantBits); // UUID never generated on this entity before it was persisted } else if(this.getId() != null) { this.uuid = new UUID(this.getId(), this.getId()); // UUID never accessed on this not yet persisted entity } else { this.setUuid(UUID.randomUUID()); } return this.uuid; } private void setUuid(UUID uuid) { if(uuid == null) { return; } // For the one hypothetical case where generated UUID could colude with UUID build from IDs if(uuid.getMostSignificantBits() == uuid.getLeastSignificantBits()) { throw new Exception("UUID: " + this.getUuid() + " format is only for internal use"); } this.uuidMostSignificantBits = uuid.getMostSignificantBits(); this.uuidLeastSignificantBits = uuid.getLeastSignificantBits(); this.uuid = uuid; } 

In practice it seems, that Option 2 (Primary key) is most frequently used. Natural and IMMUTABLE business key is seldom thing, creating and supporting synthetic keys are too heavy to solve situations, which are probably never happened. Have a look at spring-data-jpa AbstractPersistable implementation (the only thing: for Hibernate implementation use Hibernate.getClass ).

 public boolean equals(Object obj) { if (null == obj) { return false; } if (this == obj) { return true; } if (!getClass().equals(ClassUtils.getUserClass(obj))) { return false; } AbstractPersistable<?> that = (AbstractPersistable<?>) obj; return null == this.getId() ? false : this.getId().equals(that.getId()); } @Override public int hashCode() { int hashCode = 17; hashCode += null == getId() ? 0 : getId().hashCode() * 31; return hashCode; } 

Just aware of manipulating new objects in HashSet/HashMap. In opposite, the Option 1 (remain Object implementation) is broken just after merge , that is very common situation.

If you have no business key and have a REAL needs to manipulate new entity in hash structure, override hashCode to constant, as below Vlad Mihalcea was advised.

Below is a simple (and tested) solution for Scala.

  • Note that this solution does not fit into any of the 3 categories given in the question.

  • All my Entities are subclasses of the UUIDEntity so I follow the don't-repeat-yourself (DRY) principle.

  • If needed the UUID generation can be made more precise (by using more pseudo-random numbers).

Scala Code:

 import javax.persistence._ import scala.util.Random @Entity @Inheritance(strategy = InheritanceType.TABLE_PER_CLASS) abstract class UUIDEntity { @Id @GeneratedValue(strategy = GenerationType.TABLE) var id:java.lang.Long=null var uuid:java.lang.Long=Random.nextLong() override def equals(o:Any):Boolean= o match{ case o : UUIDEntity => o.uuid==uuid case _ => false } override def hashCode() = uuid.hashCode() }