Уау... след още един ден борба с това, мисля, че съм прегърнал това нещо. Този отговор обхваща малко повече от първоначалното описание на въпроса, но това е, защото открих още проблеми, след като преодолех проблема с генератора на Hibernate.
Проблем №1:Получаване на Oracle GUID()
стойност
Както е описано в отговора на Адам Хоукс, генераторът на Hibernate "guid" не се поддържа и работи само за по-стари версии на диалекта на Oracle.
Въпреки това, ако използвате генератора на Hibernate "присвоен" (което означава, че искате да зададете първични ключове ръчно, вместо Hibernate да ги генерира автоматично), тогава можете да вмъкнете стойности, извлечени от Oracle SYS_GUID()
обадете се.
Въпреки че по-новите диалекти на Oracle на Hibernate не поддържат безпроблемно "guid", те все още разбират SQL, необходим за генериране на тези стойности. Ако сте вътре в контролер, можете да извлечете тази SQL заявка със следното:
String guidSQL = grailsApplication.getMainContext().sessionFactory.getDialect().getSelectGUIDString()
Ако вместо това сте вътре в домейн клас, пак можете да направите това... но ще трябва първо да инжектирате препратка към grailsApplication. Вероятно обаче искате да направите това в контролер... повече за това по-долу.
Ако сте любопитни, действителният низ, върнат тук (за Oracle), е:
select rawtohex(sys_guid()) from dual
Можете да изпълните този SQL и да извлечете генерираната ID стойност по този начин:
String guid = grailsApplication.getMainContext().sessionFactory.currentSession.createSQLQuery(guidSQL).list().get(0)
Проблем #2:Действително използване на тази стойност в обект на домейн на Grails
За да използвате действително тази стойност на GUID във вашия клас на домейн на Grails, трябва да използвате "присвоения" генератор на Hibernate. Както споменахме по-рано, това декларира, че искате да зададете вашите собствени идентификатори ръчно, вместо да позволите на Grails/GORM/Hibernate да ги генерира автоматично. Сравнете този модифициран кодов фрагмент с този в първоначалния ми въпрос по-горе:
...
static mapping = {
table name: "OWNER"
version false
id column: "OWNER_OID", generator: "assigned"
name column: "NAME"
...
}
...
В моя клас на домейн промених "guid" на "assigned". Открих също, че трябва да премахна „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, които пречат на задаването на ID в "beforeInsert()
" правилно. За съжаление ще трябва да запазите тази логика на ниво контролер.
Проблем #3:Накарайте Grails/GORM/Hibernate да съхранява това правилно
Чистата истина е, че Grails е предназначен предимно за първично нови приложения и поддръжката му за наследени бази данни е доста петна (честно казано, все пак е малко по-малко петна от други „динамични“ рамки, които съм опитвал ). Дори ако използвате "присвоения" генератор, Grails понякога се обърква, когато продължи да запазва обекта на домейна.
Един такъв проблем е, че ".save()
" извикването понякога се опитва да направи UPDATE, когато трябва да прави INSERT. Забележете, че във фрагмента на контролера по-горе добавих "insert: true
" като параметър към ".save()
" повикване. Това казва изрично на Grails/GORM/Hibernate да опита операция INSERT, а не UPDATE.
Всички звезди и планети трябва да са в една линия, за да работи това правилно. Ако вашият клас на домейн "static mapping {}
" не задава генератора на Hibernate на "assigned
“ и също така задайте „version false
", тогава Grails/GORM/Hibernate пак ще се обърка и ще се опита да издаде АКТУАЛИЗАЦИЯ, а не INSERT.
Ако използвате автоматично генерирани контролери на Grails Scaffolding, тогава е безопасно да използвате „insert: true
" в "save()
на контролера ", тъй като тази функция се извиква само при записване на нов обект за първи път. Когато потребител редактира съществуващ обект, "update()
на контролера " се използва вместо това. Въпреки това, ако правите свои собствени неща в собствен персонализиран код някъде... ще бъде важно да проверите дали обект на домейн вече е в базата данни, преди да направите ".save()
" и предава само "insert: true
", ако наистина е вмъкване за първи път.
Проблем #4:Използване на естествени ключове с Grails/GORM/Hibernate
Една последна бележка, която не е свързана със стойностите на Oracle GUID, но е свързана с тези проблеми с Grails като цяло. Да кажем, че в наследена база данни (като тази, с която се занимавам), някои от вашите таблици използват естествен ключ като първичен ключ. Кажете, че имате OWNER_TYPE
таблица, съдържаща всички възможни "типове" на OWNER
и NAME
колона е както четим от човека идентификатор, така и първичен ключ.
Ще трябва да направите няколко други неща, за да работи това с Grails Scaffolding. От една страна, автоматично генерираните изгледи не показват ID полето на екрана, когато потребителите създават нови обекти. Ще трябва да вмъкнете някакъв HTML към съответния изглед, за да добавите поле за ID. Ако дадете на полето име „id
", тогава функцията "save()" на автоматично генерирания контролер ще получи тази стойност като "params.id
".
Второ, трябва да се уверите, че автоматично генерираният контролер "save()
" правилно вмъква стойността на ID. Когато се генерира за първи път, "save()" започва с инстанциране на домейн обект от CGI параметрите, предадени от View:
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“ е да разберете защо сте се подложили на цялата тази болка, вместо просто да напишете своя CRUD интерфейс на ръка с Spring Web MVC (или дори ванилен JSP) на първо място! :)