Uff... po kolejnym dniu zmagania się z tym, myślę, że trzymam to w ramionach. Ta odpowiedź obejmuje nieco więcej podstaw niż pierwotny opis pytania, ale to dlatego, że po ominięciu problemu z generatorem Hibernate napotkałem jeszcze więcej problemów.
Problem #1:Pobieranie Oracle GUID()
wartość
Zgodnie z odpowiedzią Adama Hawkesa, generator Hibernate „guid” nie jest konserwowany i działa tylko ze starszymi wersjami dialektu Oracle.
Jeśli jednak używasz „przypisanego” generatora Hibernate (co oznacza, że chcesz ustawić klucze podstawowe ręcznie, zamiast automatycznego generowania ich przez Hibernate), możesz wstawić wartości pobrane z Oracle SYS_GUID()
zadzwoń.
Mimo że nowsze dialekty Oracle w Hibernate nie obsługują bezproblemowo „guid”, nadal rozumieją SQL niezbędny do wygenerowania tych wartości. Jeśli jesteś w kontrolerze, możesz pobrać to zapytanie SQL w następujący sposób:
String guidSQL = grailsApplication.getMainContext().sessionFactory.getDialect().getSelectGUIDString()
Jeśli zamiast tego znajdujesz się w klasie domeny, nadal możesz to zrobić... ale najpierw musisz wstrzyknąć referencję do grailsApplication. Prawdopodobnie chcesz to zrobić w kontrolerze, ale... więcej na ten temat poniżej.
Jeśli jesteś ciekawy, rzeczywisty ciąg zwrócony tutaj (dla Oracle) to:
select rawtohex(sys_guid()) from dual
Możesz wykonać ten kod SQL i pobrać wygenerowany identyfikator w następujący sposób:
String guid = grailsApplication.getMainContext().sessionFactory.currentSession.createSQLQuery(guidSQL).list().get(0)
Problem 2:Faktycznie używam tej wartości w obiekcie domeny Grails
Aby faktycznie użyć tej wartości GUID w swojej klasie domeny Grails, musisz użyć generatora hibernacji "przypisane". Jak wspomniano wcześniej, oznacza to, że chcesz ustawić swój własny identyfikator ręcznie, zamiast pozwolić, aby Grails/GORM/Hibernacja generowały je automatycznie. Porównaj ten zmodyfikowany fragment kodu z tym w moim pierwotnym pytaniu powyżej:
...
static mapping = {
table name: "OWNER"
version false
id column: "OWNER_OID", generator: "assigned"
name column: "NAME"
...
}
...
W mojej klasie domeny zmieniłem „guid” na „assigned”. Zauważyłem również, że muszę wyeliminować „columns {}
" blok grupowania i przenieś wszystkie informacje o moich kolumnach na wyższy poziom (dziwne).
Teraz, niezależnie od kontrolera, który tworzy te obiekty domeny... wygeneruj identyfikator GUID, jak opisano powyżej, i podłącz go do obiektu „id
". W kontrolerze wygenerowanym automatycznie przez rusztowanie Grails, funkcja będzie miała postać "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)
}
Możesz spróbować umieścić tę logikę bezpośrednio w obiekcie domeny, w „beforeInsert()
". To z pewnością byłoby czystsze i bardziej eleganckie, ale istnieją pewne znane błędy w Grails, które uniemożliwiają ustawienie identyfikatora w "beforeInsert()
" prawidłowo. Niestety musisz zachować tę logikę na poziomie kontrolera.
Problem nr 3:Spraw, aby Grails/GORM/Hibernacja prawidłowo przechowywały dane
Prawda jest taka, że Grails jest przeznaczony głównie dla nowych, dziewiczych aplikacji, a jego obsługa starszych baz danych jest dość nieregularna (jednakże jest nieco mniej niestabilna niż inne „dynamiczne” frameworki, które wypróbowałem ). Nawet jeśli używasz "przypisanego" generatora, Grails czasami się myli, gdy chce utrwalić obiekt domeny.
Jednym z takich problemów jest to, że „.save()
Wywołanie czasami próbuje wykonać UPDATE, gdy powinno wykonywać INSERT. Zauważ, że we fragmencie kontrolera powyżej dodałem "insert: true
" jako parametr do ".save()
". To mówi Grails/GORM/Hibernacja, aby spróbował wykonać operację INSERT zamiast UPDATE.
Aby wszystko działało prawidłowo, wszystkie gwiazdy i planety muszą być w jednej linii. Jeśli klasa Twojej domeny „static mapping {}
blok " nie ustawia generatora hibernacji na "assigned
", a także ustaw "version false
", Grails/GORM/Hibernacja nadal będzie się mylić i spróbuje wydać UPDATE zamiast INSERT.
Jeśli używasz automatycznie generowanych kontrolerów rusztowań Grails, możesz bezpiecznie użyć "insert: true
" w kontrolerze "save()
", ponieważ ta funkcja jest wywoływana tylko podczas zapisywania nowego obiektu po raz pierwszy. Gdy użytkownik edytuje istniejący obiekt, funkcja "update()
kontrolera zamiast tego używana jest funkcja ". Jednakże, jeśli robisz swoje własne rzeczy w swoim własnym, niestandardowym kodzie gdzieś... ważne będzie sprawdzenie, czy obiekt domeny jest już w bazie danych, zanim zrobisz ".save()
wywołaj " i przekaż tylko "insert: true
", jeśli naprawdę jest to wstawianie po raz pierwszy.
Problem 4:Używanie naturalnych klawiszy w Grails/GORM/Hibernacja
Ostatnia uwaga, nie mająca nic wspólnego z wartościami Oracle GUID, ale ogólnie związana z tymi problemami z Grails. Załóżmy, że w starszej bazie danych (takiej jak ta, z którą miałem do czynienia), niektóre tabele używają klucza naturalnego jako klucza podstawowego. Załóżmy, że masz OWNER_TYPE
tabela zawierająca wszystkie możliwe "typy" OWNER
i NAME
kolumna jest zarówno identyfikatorem czytelnym dla człowieka, jak i kluczem podstawowym.
Będziesz musiał zrobić kilka innych rzeczy, aby to zadziałało z rusztowaniem Grails. Po pierwsze, automatycznie generowane widoki nie pokazują pola identyfikatora na ekranie, gdy użytkownicy tworzą nowe obiekty. Będziesz musiał wstawić kod HTML do odpowiedniego widoku, aby dodać pole dla identyfikatora. Jeśli nadasz polu nazwę „id
”, wówczas automatycznie wygenerowana funkcja kontrolera „save()” otrzyma tę wartość jako „params.id
".
Po drugie, musisz upewnić się, że automatycznie wygenerowana funkcja kontrolera "save()
" funkcja poprawnie wstawia wartość identyfikatora. Po pierwszym wygenerowaniu funkcja "save()" rozpoczyna się od utworzenia instancji obiektu domeny z parametrów CGI przekazanych przez widok:
def ownerTypeInstance = new OwnerType.get( params )
Nie obsługuje to jednak pola identyfikatora dodanego do widoku. Nadal będziesz musiał ustawić to ręcznie. Jeśli w Widoku nadałeś polu HTML nazwę „id
", wtedy będzie dostępny w "save()
" jako "params.id
":
...
ownerTypeInstance = new OwnerType()
ownerTypeInstance.id = params.id
// Proceed to the ".save()" step, making sure to pass "insert: true"
...
Bułka z masłem, co? Być może „Problem nr 5” polega na ustaleniu, dlaczego narażasz się na ten cały ból, zamiast po prostu pisać ręcznie interfejs CRUD za pomocą Spring Web MVC (lub nawet waniliowych JSP)! :)