Accidenti... dopo un altro giorno di lotta con questo, penso di avere le braccia intorno alla cosa. Questa risposta copre un po' più di terreno rispetto alla descrizione della domanda originale, ma è perché ho riscontrato ancora più problemi dopo aver superato il problema del generatore di ibernazione.
Problema n. 1:ottenere un GUID()
Oracle valore
Come coperto dalla risposta di Adam Hawkes, il generatore di Hibernate "guid" non è mantenuto e funziona solo per le versioni precedenti del dialetto Oracle.
Tuttavia, se utilizzi il generatore di Hibernate "assegnato" (il che significa che vuoi impostare manualmente le chiavi primarie invece di farle generare automaticamente da Hibernate), puoi inserire valori estratti da un Oracle SYS_GUID()
chiama.
Anche se i nuovi dialetti Oracle di Hibernate non supportano "guid" senza problemi, comprendono comunque l'SQL necessario per generare questi valori. Se sei all'interno di un controller, puoi recuperare quella query SQL con quanto segue:
String guidSQL = grailsApplication.getMainContext().sessionFactory.getDialect().getSelectGUIDString()
Se invece sei all'interno di una classe di dominio, puoi ancora farlo... ma dovrai prima inserire un riferimento a grailsApplication. Probabilmente vorrai farlo in un controller, però... più su questo sotto.
Se sei curioso, la stringa effettiva restituita qui (per Oracle) è:
select rawtohex(sys_guid()) from dual
Puoi eseguire questo SQL e recuperare il valore ID generato in questo modo:
String guid = grailsApplication.getMainContext().sessionFactory.currentSession.createSQLQuery(guidSQL).list().get(0)
Problema n. 2:utilizzo effettivo di questo valore in un oggetto di dominio Grails
Per utilizzare effettivamente questo valore GUID nella tua classe di dominio Grails, devi utilizzare il generatore di Hibernate "assegnato". Come accennato in precedenza, questo dichiara che vuoi impostare i tuoi ID manualmente, piuttosto che lasciare che Grails/GORM/Hibernate li generi automaticamente. Confronta questo frammento di codice modificato con quello nella mia domanda originale sopra:
...
static mapping = {
table name: "OWNER"
version false
id column: "OWNER_OID", generator: "assigned"
name column: "NAME"
...
}
...
Nella mia classe di dominio, ho cambiato "guid" in "assegnato". Ho anche scoperto che dovevo eliminare le "columns {}
" blocco di raggruppamento e sposta tutte le informazioni della mia colonna su un livello (strano).
Ora, in qualsiasi Controller stia creando questi oggetti di dominio... genera un GUID come descritto sopra e collegalo all'"id
dell'oggetto ". In un controller generato automaticamente da Grails Scaffolding, la funzione sarà "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)
}
Potresti pensare di provare a inserire questa logica direttamente all'interno dell'oggetto dominio, in un "beforeInsert()
" funzione. Sarebbe sicuramente più pulito ed elegante, ma ci sono alcuni bug noti con Grail che impediscono l'impostazione degli ID in "beforeInsert()
" correttamente. Purtroppo, dovrai mantenere questa logica a livello di controller.
Problema n. 3:fai in modo che Grails/GORM/Hibernate lo memorizzi correttamente
La semplice verità è che Grails è destinato principalmente a nuove applicazioni vergini e il suo supporto per i database legacy è piuttosto impreciso (in tutta onestà, però, è un po' meno imprevedibile rispetto ad altri framework "dinamici" che ho provato ). Anche se usi il generatore "assegnato", Grails a volte si confonde quando va a rendere persistente l'oggetto di dominio.
Uno di questi problemi è che un ".save()
" call a volte tenta di eseguire un UPDATE quando dovrebbe eseguire un INSERT. Notare che nello snippet Controller sopra, ho aggiunto "insert: true
" come parametro per ".save()
" call. Questo dice a Grails/GORM/Hibernate esplicitamente di tentare un'operazione INSERT anziché UPDATE.
Tutte le stelle e i pianeti devono essere allineati affinché funzioni correttamente. Se la tua classe di dominio "static mapping {}
" block non imposta il generatore di ibernazione su "assigned
", e imposta anche "version false
", quindi Grails/GORM/Hibernate continueranno a confondersi e proveranno a emettere un AGGIORNAMENTO anziché un INSERTO.
Se stai usando controller Grails Scaffolding generati automaticamente, puoi usare "insert: true
" nel "save()
del Titolare " funzione, perché quella funzione viene chiamata solo quando si salva un nuovo oggetto per la prima volta. Quando un utente modifica un oggetto esistente, il controller "update()
viene invece utilizzata la funzione ". Tuttavia, se stai facendo le tue cose nel tuo codice personalizzato da qualche parte... sarà importante controllare se un oggetto di dominio è già nel database prima di creare un ".save()
" chiama e passa solo "insert: true
" parametro se è davvero un inserimento per la prima volta.
Problema n. 4:utilizzo delle chiavi naturali con Grail/GORM/Ibernazione
Un'ultima nota, che non ha a che fare con i valori di Oracle GUID, ma correlata a questi problemi di Grail in generale. Diciamo che in un database legacy (come quello con cui ho avuto a che fare), alcune delle tue tabelle usano una chiave naturale come chiave primaria. Supponi di avere un OWNER_TYPE
tabella, contenente tutti i possibili "tipi" di OWNER
e il NAME
colonna è sia l'identificatore leggibile che la chiave primaria.
Dovrai fare un paio di altre cose per farlo funzionare con Grails Scaffolding. Per prima cosa, le viste generate automaticamente non mostrano il campo ID sullo schermo quando gli utenti creano nuovi oggetti. Dovrai inserire del codice HTML nella vista pertinente per aggiungere un campo per l'ID. Se assegni al campo un nome di "id
", la funzione "save()" del controller generata automaticamente riceverà questo valore come "params.id
".
In secondo luogo, devi assicurarti che "save()
del controller generato automaticamente La funzione " inserisce correttamente il valore dell'ID. Quando viene generata per la prima volta, un "save()" inizia istanziando un oggetto di dominio dai parametri CGI passati dalla View:
def ownerTypeInstance = new OwnerType.get( params )
Tuttavia, questo non gestisce il campo ID che hai aggiunto alla tua vista. Dovrai comunque impostarlo manualmente. Se nella vista hai assegnato al campo HTML un nome di "id
", quindi sarà disponibile in "save()
" come "params.id
":
...
ownerTypeInstance = new OwnerType()
ownerTypeInstance.id = params.id
// Proceed to the ".save()" step, making sure to pass "insert: true"
...
Pezzo di torta, eh? Forse "Problema n. 5" sta cercando di capire perché ti sei sottoposto a tutto questo dolore, piuttosto che scrivere semplicemente la tua interfaccia CRUD a mano con Spring Web MVC (o anche JSP vaniglia) in primo luogo! :)