Usch... efter ännu en dag av brottning med det här, tror jag att jag har armarna runt saken. Det här svaret täcker lite mer mark än den ursprungliga frågebeskrivningen, men det beror på att jag hittade ännu fler problem efter att ha kommit förbi problemet med Hibernate-generatorn.
Problem #1:Skaffa en Oracle GUID()
värde
Som täcks av Adam Hawkes svar, är "guid" Hibernate-generatorn inte underhållen och fungerar endast för äldre versioner av Oracle-dialekten.
Men om du använder Hibernate-generatorn "tilldelad" (vilket innebär att du vill ställa in primärnycklar manuellt istället för att Hibernate ska generera dem automatiskt), så kan du infoga värden hämtade från en Oracle SYS_GUID()
ring.
Även om Hibernates nyare Oracle-dialekter inte stöder "guid" sömlöst, förstår de fortfarande den SQL som krävs för att generera dessa värden. Om du är inne i en Controller kan du hämta den SQL-frågan med följande:
String guidSQL = grailsApplication.getMainContext().sessionFactory.getDialect().getSelectGUIDString()
Om du istället är inne i en domänklass kan du fortfarande göra detta... men du måste först injicera en referens till grailsApplication. Du vill antagligen göra detta i en Controller, men... mer om detta nedan.
Om du är nyfiken är den faktiska strängen som returneras här (för Oracle):
select rawtohex(sys_guid()) from dual
Du kan köra denna SQL och hämta det genererade ID-värdet så här:
String guid = grailsApplication.getMainContext().sessionFactory.currentSession.createSQLQuery(guidSQL).list().get(0)
Problem #2:Att faktiskt använda detta värde i ett Grails-domänobjekt
För att faktiskt använda detta GUID-värde i din Grails-domänklass måste du använda Hibernate-generatorn "tilldelad". Som nämnts tidigare förklarar detta att du vill ställa in dina egna ID manuellt, istället för att låta Grails/GORM/Hibernate generera dem automatiskt. Jämför detta modifierade kodavsnitt med det i min ursprungliga fråga ovan:
...
static mapping = {
table name: "OWNER"
version false
id column: "OWNER_OID", generator: "assigned"
name column: "NAME"
...
}
...
I min domänklass ändrade jag "guide" till "tilldelad". Jag upptäckte också att jag behövde ta bort "columns {}
" grupperingsblock och flytta all min kolumninformation upp en nivå (konstigt).
Nu, i vilken Controller som än skapar dessa domänobjekt... generera en GUID enligt beskrivningen ovan och anslut den till objektets "id
"-fältet. I en styrenhet som genereras automatiskt av Grails Scaffolding kommer funktionen att vara "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)
}
Du kanske funderar på att försöka placera denna logik direkt inuti domänobjektet, i en "beforeInsert()
"-funktion. Det skulle definitivt vara renare och mer elegant, men det finns några kända buggar med Grails som förhindrar att ID:n ställs in i "beforeInsert()
" korrekt. Tyvärr måste du hålla denna logik på kontrollnivån.
Problem 3:Få Grails/GORM/Hibernate att lagra detta på rätt sätt
Den enkla sanningen är att Grails främst är avsedd för nya applikationer och dess stöd för äldre databaser är ganska fläckigt (i rättvisans namn är det dock lite mindre fläckigt än andra "dynamiska" ramverk jag har provat em> ). Även om du använder den "tilldelade" generatorn, blir Grails ibland förvirrad när det går att bevara domänobjektet.
Ett sådant problem är att en ".save()
" call försöker ibland göra en UPPDATERING när det borde göra en INSERT. Lägg märke till att jag har lagt till "insert: true
i Controller-kodavsnittet ovan. " som en parameter till ".save()
"-anrop. Detta säger till Grails/GORM/Hibernate uttryckligen att försöka en INSERT-operation snarare än en UPPDATERA.
Alla stjärnor och planeter måste vara i linje för att detta ska fungera rätt. Om din domän klass "static mapping {}
" blocket ställer inte in vilolägesgeneratorn till "assigned
", och även ange "version false
", då kommer Grails/GORM/Hibernate fortfarande att bli förvirrad och försöka skicka en UPPDATERING snarare än en INFOGA.
Om du använder automatiskt genererade Grails Scaffolding-kontroller är det säkert att använda "insert: true
" i Controllerns "save()
"-funktionen, eftersom den funktionen endast anropas när ett nytt objekt sparas för första gången. När en användare redigerar ett befintligt objekt, kontrolleras "update()
"-funktionen används istället. Men om du gör din egen sak i din egen anpassade kod någonstans... kommer det att vara viktigt att kontrollera om ett domänobjekt redan finns i databasen innan du gör en ".save()
" anrop och skicka bara "insert: true
" parameter om det verkligen är en förstagångsinsättning.
Problem #4:Använda naturliga nycklar med Grails/GORM/Hibernate
En sista anmärkning, som inte har att göra med Oracle GUID-värden, utan relaterad till dessa Grails-problem i allmänhet. Låt oss säga att i en äldre databas (som den jag har sysslat med) använder vissa av dina tabeller en naturlig nyckel som primärnyckel. Säg att du har en OWNER_TYPE
tabell, som innehåller alla möjliga "typer" av OWNER
och NAME
kolumnen är både den läsbara identifieraren och primärnyckeln.
Du måste göra ett par andra saker för att få det här att fungera med Grails Scaffolding. För det första visar de automatiskt genererade vyerna inte ID-fältet på skärmen när användare skapar nya objekt. Du måste infoga lite HTML i relevant vy för att lägga till ett fält för ID:t. Om du ger fältet namnet "id
", då kommer den automatiskt genererade styrenhetens "save()"-funktion att få detta värde som "params.id
".
För det andra måste du se till att den automatiskt genererade styrenhetens "save()
"-funktionen infogar ID-värdet korrekt. När den först genereras börjar en "save()" med att instansiera ett domänobjekt från CGI-parametrarna som skickas av vyn:
def ownerTypeInstance = new OwnerType.get( params )
Detta hanterar dock inte ID-fältet du har lagt till i din vy. Du måste fortfarande ställa in det manuellt. Om du i vyn gav HTML-fältet namnet "id
", då blir den tillgänglig i "save()
" som "params.id
":
...
ownerTypeInstance = new OwnerType()
ownerTypeInstance.id = params.id
// Proceed to the ".save()" step, making sure to pass "insert: true"
...
En tårta, va? Kanske är "Problem #5" att ta reda på varför du utsätter dig för all denna smärta, snarare än att bara skriva ditt CRUD-gränssnitt för hand med Spring Web MVC (eller till och med vanilla JSP:s) i första hand! :)