Oef... na weer een dag worstelen met dit, denk ik dat ik mijn armen om het ding heb. Dit antwoord dekt iets meer grond dan de oorspronkelijke vraagbeschrijving, maar dat komt omdat ik nog meer problemen heb gevonden nadat ik voorbij het probleem van de Hibernate-generator was gekomen.
Probleem #1:een Oracle GUID()
verkrijgen waarde
Zoals beschreven in het antwoord van Adam Hawkes, wordt de "guid" Hibernate-generator niet onderhouden en werkt deze alleen voor oudere versies van het Oracle-dialect.
Als u echter de Hibernate-generator "toegewezen" gebruikt (wat betekent dat u de primaire sleutels handmatig wilt instellen in plaats van dat de Hibernate ze automatisch genereert), kunt u waarden invoegen die zijn opgehaald uit een Oracle SYS_GUID()
bellen.
Hoewel de nieuwere Oracle-dialecten van Hibernate "guid" niet naadloos ondersteunen, begrijpen ze nog steeds de SQL die nodig is om deze waarden te genereren. Als u zich in een controller bevindt, kunt u die SQL-query als volgt ophalen:
String guidSQL = grailsApplication.getMainContext().sessionFactory.getDialect().getSelectGUIDString()
Als je in plaats daarvan in een domeinklasse zit, kun je dit nog steeds doen... maar je moet eerst een verwijzing naar grailsApplication invoegen. Je wilt dit waarschijnlijk in een Controller doen, maar... meer hierover hieronder.
Als je nieuwsgierig bent, is de daadwerkelijke String die hier wordt geretourneerd (voor Oracle):
select rawtohex(sys_guid()) from dual
U kunt deze SQL uitvoeren en de gegenereerde ID-waarde als volgt ophalen:
String guid = grailsApplication.getMainContext().sessionFactory.currentSession.createSQLQuery(guidSQL).list().get(0)
Probleem #2:Deze waarde daadwerkelijk gebruiken in een Grails-domeinobject
Om deze GUID-waarde daadwerkelijk in uw Grails-domeinklasse te gebruiken, moet u de Hibernate-generator "toegewezen" gebruiken. Zoals eerder vermeld, verklaart dit dat u uw eigen ID's handmatig wilt instellen, in plaats van Grails/GORM/Hibernate ze automatisch te laten genereren. Vergelijk dit gewijzigde codefragment met dat in mijn oorspronkelijke vraag hierboven:
...
static mapping = {
table name: "OWNER"
version false
id column: "OWNER_OID", generator: "assigned"
name column: "NAME"
...
}
...
In mijn domeinklasse heb ik "guid" gewijzigd in "toegewezen". Ik ontdekte ook dat ik de "columns {}
. moest verwijderen " groepeerblok, en verplaats al mijn kolominformatie een niveau omhoog (raar).
Nu, in welke controller deze domeinobjecten ook maakt... genereer een GUID zoals hierboven beschreven, en plug deze in de "id
van het object" " veld. In een Controller die automatisch wordt gegenereerd door Grails Scaffolding, is de functie "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)
}
Je zou kunnen denken om deze logica direct in het domeinobject te plaatsen, in een "beforeInsert()
" functie. Dat zou zeker schoner en eleganter zijn, maar er zijn enkele bekende bugs met Grails die voorkomen dat ID's worden ingesteld in "beforeInsert()
" correct. Helaas moet je deze logica op het controller-niveau houden.
Probleem #3:Zorg dat Grails/GORM/Hibernate dit correct opslaat
De waarheid is dat Grails in de eerste plaats bedoeld is voor nieuwe applicaties, en de ondersteuning voor legacy-databases is behoorlijk vlekkerig (in alle eerlijkheid is het echter een beetje minder vlekkerig dan andere "dynamische" frameworks die ik heb geprobeerd em> ). Zelfs als u de "toegewezen" generator gebruikt, raakt Grails soms in de war wanneer het het domeinobject gaat behouden.
Een van die problemen is dat een ".save()
" call probeert soms een UPDATE uit te voeren terwijl het een INSERT zou moeten doen. Merk op dat ik in het bovenstaande Controller-fragment "insert: true
heb toegevoegd " als een parameter naar de ".save()
" aanroep. Dit vertelt Grails/GORM/Hibernate expliciet om een INSERT-bewerking te proberen in plaats van een UPDATE-bewerking.
Alle sterren en planeten moeten op één lijn staan om dit goed te laten werken. Als uw domeinklasse "static mapping {}
" blok stelt de slaapstandgenerator niet in op "assigned
", en stel ook "version false
. in ", dan zullen Grails/GORM/Hibernate nog steeds in de war raken en proberen een UPDATE uit te geven in plaats van een INSERT.
Als u automatisch gegenereerde Grails Steigercontrollers gebruikt, is het veilig om "insert: true
te gebruiken " in de "save()
. van de Controller" " functie, omdat die functie alleen wordt aangeroepen wanneer een nieuw object voor de eerste keer wordt opgeslagen. Wanneer een gebruiker een bestaand object bewerkt, wordt de "update()
van de Controller " wordt in plaats daarvan gebruikt. Als u echter ergens uw eigen ding doet in uw eigen aangepaste code... is het belangrijk om te controleren of een domeinobject al in de database staat voordat u een ".save()
" aanroepen, en alleen de "insert: true
. doorgeven " parameter als het echt een eerste keer invoegen is.
Probleem #4:Natuurlijke sleutels gebruiken met Grails/GORM/Hibernate
Een laatste opmerking, die niet te maken heeft met Oracle GUID-waarden, maar met betrekking tot deze Grails-problemen in het algemeen. Laten we zeggen dat in een verouderde database (zoals degene waar ik mee te maken heb), sommige van uw tabellen een natuurlijke sleutel als hun primaire sleutel gebruiken. Stel dat u een OWNER_TYPE
. heeft tabel met alle mogelijke "types" van OWNER
, en de NAME
kolom is zowel de door mensen leesbare identifier als de primaire sleutel.
Je zult een aantal andere dingen moeten doen om dit te laten werken met Grails Scaffolding. Om te beginnen tonen de automatisch gegenereerde weergaven het ID-veld niet op het scherm wanneer gebruikers nieuwe objecten maken. U moet wat HTML in de relevante weergave invoegen om een veld voor de ID toe te voegen. Als u het veld de naam "id
geeft ", dan zal de automatisch gegenereerde "save()"-functie van de Controller deze waarde ontvangen als "params.id
".
Ten tweede moet je ervoor zorgen dat de automatisch gegenereerde controller "save()
" functie voegt de ID-waarde correct in. Wanneer deze voor het eerst wordt gegenereerd, begint een "save()" met het instantiëren van een domeinobject op basis van de CGI-parameters die door de View zijn doorgegeven:
def ownerTypeInstance = new OwnerType.get( params )
Dit is echter niet van toepassing op het ID-veld dat u aan uw weergave hebt toegevoegd. U moet dat nog steeds handmatig instellen. Als u in de weergave het HTML-veld de naam "id
. heeft gegeven ", dan zal het beschikbaar zijn in "save()
" als "params.id
":
...
ownerTypeInstance = new OwnerType()
ownerTypeInstance.id = params.id
// Proceed to the ".save()" step, making sure to pass "insert: true"
...
Een fluitje van een cent, hè? Misschien is "Probleem #5" aan het uitzoeken waarom je jezelf al deze pijn hebt aangedaan, in plaats van alleen je CRUD-interface met de hand te schrijven met Spring Web MVC (of zelfs vanille JSP's) in de eerste plaats! :)