वाह... इसके साथ कुश्ती के एक और दिन के बाद, मुझे लगता है कि इस चीज़ के चारों ओर मेरी बाहें हैं। यह उत्तर मूल प्रश्न विवरण की तुलना में थोड़ा अधिक जमीन को कवर करता है, लेकिन ऐसा इसलिए है क्योंकि मुझे हाइबरनेट जनरेटर समस्या को पार करने के बाद और भी समस्याएं मिलीं।
समस्या #1:Oracle प्राप्त करना GUID()
मान
जैसा कि एडम हॉक्स के उत्तर द्वारा कवर किया गया है, "गाइड" हाइबरनेट जनरेटर का रखरखाव नहीं किया गया है और केवल Oracle बोली के पुराने संस्करणों के लिए काम करता है।
हालाँकि, यदि आप हाइबरनेट जनरेटर "असाइन" का उपयोग करते हैं (जिसका अर्थ है कि आप हाइबरनेट को ऑटो-जेनरेट करने के बजाय मैन्युअल रूप से प्राथमिक कुंजी सेट करना चाहते हैं), तो आप Oracle SYS_GUID()
से खींचे गए मान सम्मिलित कर सकते हैं। कॉल करें।
भले ही हाइबरनेट की नई Oracle बोलियाँ "गाइड" को मूल रूप से समर्थन नहीं करती हैं, फिर भी वे इन मानों को उत्पन्न करने के लिए आवश्यक SQL को समझते हैं। यदि आप नियंत्रक के अंदर हैं, तो आप निम्न के साथ उस SQL क्वेरी को प्राप्त कर सकते हैं:
String guidSQL = grailsApplication.getMainContext().sessionFactory.getDialect().getSelectGUIDString()
यदि आप इसके बजाय किसी डोमेन वर्ग के अंदर हैं, तब भी आप ऐसा कर सकते हैं... लेकिन आपको पहले grailsApplication का संदर्भ देना होगा। आप शायद इसे नियंत्रक में करना चाहते हैं, हालांकि... इस पर और अधिक जानकारी नीचे दी गई है।
यदि आप उत्सुक हैं, तो वास्तविक स्ट्रिंग यहाँ (Oracle के लिए) लौटा है:
select rawtohex(sys_guid()) from dual
आप इस SQL को निष्पादित कर सकते हैं और इस तरह से उत्पन्न आईडी मान प्राप्त कर सकते हैं:
String guid = grailsApplication.getMainContext().sessionFactory.currentSession.createSQLQuery(guidSQL).list().get(0)
समस्या #2:वास्तव में Grails डोमेन ऑब्जेक्ट में इस मान का उपयोग करना
वास्तव में अपने Grails डोमेन वर्ग में इस GUID मान का उपयोग करने के लिए, आपको हाइबरनेट जेनरेटर "असाइन किया गया" का उपयोग करने की आवश्यकता है। जैसा कि पहले उल्लेख किया गया है, यह घोषणा करता है कि आप अपनी खुद की आईडी को मैन्युअल रूप से सेट करना चाहते हैं, बजाय इसके कि Grails/GORM/Hibernate उन्हें स्वचालित रूप से उत्पन्न करें। इस संशोधित कोड स्निपेट की तुलना ऊपर दिए गए मेरे मूल प्रश्न से करें:
...
static mapping = {
table name: "OWNER"
version false
id column: "OWNER_OID", generator: "assigned"
name column: "NAME"
...
}
...
अपने डोमेन वर्ग में, मैंने "गाइड" को "असाइन" में बदल दिया। मैंने यह भी पाया कि मुझे "columns {}
. को समाप्त करने की आवश्यकता है " ग्रुपिंग ब्लॉक करें, और मेरी सभी कॉलम जानकारी को एक स्तर (अजीब) तक ले जाएं।
अब, जो भी नियंत्रक इन डोमेन ऑब्जेक्ट्स को बना रहा है... ऊपर वर्णित अनुसार एक GUID उत्पन्न करें, और इसे ऑब्जेक्ट के "id
में प्लग करें। " फ़ील्ड। Grails Scaffolding द्वारा स्वचालित रूप से जेनरेट किए गए नियंत्रक में, फ़ंक्शन "save()
होगा। ":
def save() {
def ownerInstance = new Owner(params)
String guidSQL = grailsApplication.getMainContext().sessionFactory.getDialect().getSelectGUIDString()
ownerInstance.id = grailsApplication.getMainContext().sessionFactory.currentSession.createSQLQuery(guidSQL).list().get(0)
if (!ownerInstance.save(flush: true, insert: true)) {
render(view: "create", model: [ownerInstance: ownerInstance])
return
}
flash.message = message(code: 'default.created.message', args: [message(code: 'owner.label', default: 'Owner'), ownerInstance.id])
redirect(action: "show", id: ownerInstance.id)
}
आप इस तर्क को सीधे डोमेन ऑब्जेक्ट के अंदर "beforeInsert()
में डालने का प्रयास करने के बारे में सोच सकते हैं। " फ़ंक्शन। यह निश्चित रूप से क्लीनर और अधिक सुरुचिपूर्ण होगा, लेकिन Grails के साथ कुछ ज्ञात बग हैं जो आईडी को "beforeInsert()
में सेट होने से रोकते हैं। "ठीक है। दुख की बात है कि आपको इस तर्क को नियंत्रक स्तर पर रखना होगा।
समस्या #3:Grails/GORM/Hibernate को इसे ठीक से स्टोर करें
सादा सच्चाई यह है कि Grails मुख्य रूप से कुंवारी-नए अनुप्रयोगों के लिए अभिप्रेत है, और विरासत डेटाबेस के लिए इसका समर्थन बहुत धब्बेदार है (निष्पक्षता में, हालांकि, यह मेरे द्वारा आजमाए गए अन्य "गतिशील" ढांचे की तुलना में थोड़ा कम धब्बेदार है उन्हें> ) यहां तक कि अगर आप "असाइन किए गए" जनरेटर का उपयोग करते हैं, तो भी Grails कभी-कभी भ्रमित हो जाते हैं जब यह डोमेन ऑब्जेक्ट को बनाए रखने के लिए जाता है।
ऐसी ही एक समस्या है कि एक ".save()
" कॉल कभी-कभी एक अद्यतन करने का प्रयास करता है जब इसे एक INSERT करना चाहिए। ध्यान दें कि उपरोक्त नियंत्रक स्निपेट में, मैंने "insert: true
जोड़ा है। ".save()
. के पैरामीटर के रूप में " कॉल करें। यह ग्रेल्स/जीओआरएम/हाइबरनेट को एक अद्यतन के बजाय एक INSERT ऑपरेशन का प्रयास करने के लिए स्पष्ट रूप से बताता है।
इसके ठीक काम करने के लिए सभी सितारों और ग्रहों को संरेखण में होना चाहिए। यदि आपका डोमेन वर्ग "static mapping {}
" ब्लॉक हाइबरनेट जनरेटर को "assigned
. पर सेट नहीं करता है ", और "version false
. भी सेट करें ", तो Grails/GORM/Hibernate अभी भी भ्रमित हो जाएगा और INSERT के बजाय अद्यतन जारी करने का प्रयास करेगा।
यदि आप ऑटो-जेनरेटेड ग्रेल्स स्कैफोल्डिंग कंट्रोलर्स का उपयोग कर रहे हैं, तो "insert: true
का उपयोग करना सुरक्षित है। " नियंत्रक के "save()
. में " फ़ंक्शन, क्योंकि उस फ़ंक्शन को केवल पहली बार किसी नई ऑब्जेक्ट को सहेजते समय कॉल किया जाता है। जब कोई उपयोगकर्ता किसी मौजूदा ऑब्जेक्ट को संपादित करता है, तो नियंत्रक का "update()
" इसके बजाय फ़ंक्शन का उपयोग किया जाता है। हालांकि, यदि आप कहीं अपने स्वयं के कस्टम कोड में अपना काम कर रहे हैं ... यह जांचना महत्वपूर्ण होगा कि कोई डोमेन ऑब्जेक्ट पहले से ही डेटाबेस में है या नहीं इससे पहले कि आप ".save()
" कॉल करें, और केवल "insert: true
. पास करें " पैरामीटर अगर यह वास्तव में पहली बार सम्मिलित है।
समस्या #4:Grails/GORM/Hibernate के साथ प्राकृतिक कुंजियों का उपयोग करना
एक अंतिम नोट, Oracle GUID मूल्यों के साथ नहीं है, लेकिन सामान्य रूप से इन Grails मुद्दों से संबंधित है। मान लें कि एक लीगेसी डेटाबेस में (जैसे कि मैं जिसके साथ काम कर रहा हूं), आपकी कुछ तालिकाएं अपनी प्राथमिक कुंजी के रूप में एक प्राकृतिक कुंजी का उपयोग करती हैं। मान लें कि आपके पास OWNER_TYPE
है तालिका, जिसमें OWNER
. के सभी संभावित "प्रकार" शामिल हैं , और NAME
कॉलम मानव-पठनीय पहचानकर्ता के साथ-साथ प्राथमिक कुंजी दोनों है।
Grails Scaffolding के साथ इस कार्य को करने के लिए आपको कुछ अन्य कार्य करने होंगे। एक बात के लिए, ऑटो-जेनरेट किए गए दृश्य स्क्रीन पर आईडी फ़ील्ड नहीं दिखाते हैं जब उपयोगकर्ता नई वस्तुएं बना रहे होते हैं। आईडी के लिए एक फ़ील्ड जोड़ने के लिए आपको प्रासंगिक दृश्य में कुछ HTML सम्मिलित करना होगा। यदि आप फ़ील्ड को "id
. का नाम देते हैं ", फिर ऑटो-जेनरेटेड कंट्रोलर के "सेव ()" फ़ंक्शन को यह मान "params.id
के रूप में प्राप्त होगा। ".
दूसरे, आपको यह सुनिश्चित करना होगा कि ऑटो-जेनरेटेड कंट्रोलर का "save()
" फ़ंक्शन आईडी मान को ठीक से सम्मिलित करता है। जब पहली बार उत्पन्न होता है, तो "सेव ()" व्यू द्वारा पारित सीजीआई पैरामीटर से डोमेन ऑब्जेक्ट को तुरंत चालू करके शुरू होता है:
def ownerTypeInstance = new OwnerType.get( params )
हालाँकि, यह आपके द्वारा अपने दृश्य में जोड़े गए ID फ़ील्ड को हैंडल नहीं करता है। आपको अभी भी इसे मैन्युअल रूप से सेट करना होगा। यदि दृश्य पर आपने HTML फ़ील्ड को "id
. का नाम दिया है ", तब यह "save()
. में उपलब्ध होगा " के रूप में "params.id
":
...
ownerTypeInstance = new OwnerType()
ownerTypeInstance.id = params.id
// Proceed to the ".save()" step, making sure to pass "insert: true"
...
केक का टुकड़ा, हुह? शायद "समस्या # 5" यह पता लगा रहा है कि आपने अपने सीआरयूडी इंटरफ़ेस को केवल स्प्रिंग वेब एमवीसी (या यहां तक कि वेनिला जेएसपी) के साथ हाथ से लिखने के बजाय इस सारे दर्द से खुद को क्यों रखा है! :)पी>