Páni... po dalším dni zápolení s tímhle si myslím, že to mám kolem sebe. Tato odpověď pokrývá o něco více než původní popis otázky, ale to proto, že jsem po překonání problému s generátorem spánku našel ještě více problémů.
Problém č. 1:Získání Oracle GUID()
hodnotu
Jak je uvedeno v odpovědi Adama Hawkese, „guid“ generátor Hibernate je neudržovaný a funguje pouze pro starší verze dialektu Oracle.
Pokud však používáte generátor Hibernate "přiřazený" (to znamená, že chcete primární klíče nastavit ručně, místo aby je Hibernate generoval automaticky), můžete vložit hodnoty získané z Oracle SYS_GUID()
zavolejte.
I když novější dialekty Oracle Hibernate nepodporují „guid“ bez problémů, stále rozumí SQL nezbytnému pro generování těchto hodnot. Pokud jste uvnitř řadiče, můžete tento dotaz SQL načíst pomocí následujícího:
String guidSQL = grailsApplication.getMainContext().sessionFactory.getDialect().getSelectGUIDString()
Pokud se místo toho nacházíte uvnitř třídy domény, stále to můžete udělat... ale nejprve budete muset vložit odkaz na grailsApplication. Pravděpodobně to budete chtít udělat v ovladači, i když... více o tom níže.
Pokud jste zvědaví, skutečný řetězec vrácený zde (pro Oracle) je:
select rawtohex(sys_guid()) from dual
Tento SQL můžete spustit a načíst vygenerovanou hodnotu ID takto:
String guid = grailsApplication.getMainContext().sessionFactory.currentSession.createSQLQuery(guidSQL).list().get(0)
Problém č. 2:Skutečné použití této hodnoty v objektu domény Grails
Chcete-li skutečně použít tuto hodnotu GUID ve vaší třídě domény Grails, musíte použít generátor hibernace „přiřazeno“. Jak již bylo zmíněno dříve, toto deklaruje, že chcete nastavit své vlastní ID ručně, spíše než nechat Grails/GORM/Hibernate je generovat automaticky. Porovnejte tento upravený fragment kódu s tím v mé původní otázce výše:
...
static mapping = {
table name: "OWNER"
version false
id column: "OWNER_OID", generator: "assigned"
name column: "NAME"
...
}
...
V mé doménové třídě jsem změnil „guid“ na „assigned“. Také jsem zjistil, že potřebuji odstranit „columns {}
"skupinový blok a přesunout všechny informace o mém sloupci o úroveň výše (divné).
Nyní, ať už řadič vytváří tyto objekty domény... vygenerujte GUID, jak je popsáno výše, a zapojte ho do objektu "id
pole. V ovladači generovaném automaticky lešením Grails bude funkce "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žná vás napadne zkusit vložit tuto logiku přímo do objektu domény do "beforeInsert()
". To by bylo rozhodně čistší a elegantnější, ale existují některé známé chyby s Grails, které brání nastavení ID v "beforeInsert()
" správně. Bohužel tuto logiku budete muset ponechat na úrovni řadiče.
Problém č. 3:Uložte Grails/GORM/Hibernate správně
Jasnou pravdou je, že Grails je primárně určen pro zcela nové aplikace a jeho podpora starších databází je docela špinavá (ve spravedlnosti je to o něco méně špinavé než jiné „dynamické“ rámce, které jsem zkoušel em> ). I když použijete "přiřazený" generátor, Grails se někdy zmátne, když přetrvává objekt domény.
Jedním takovým problémem je, že ".save()
" call se někdy pokouší provést UPDATE, když by měl provádět INSERT. Všimněte si, že do fragmentu Controller výše jsem přidal "insert: true
" jako parametr pro ".save()
" volání. Tím sdělíte Grails/GORM/Hibernate explicitně, aby se pokusili o operaci INSERT, nikoli o operaci UPDATE.
Aby to fungovalo správně, musí být všechny hvězdy a planety zarovnány. Pokud je vaše třída domény "static mapping {}
" blok nenastaví generátor hibernace na "assigned
“ a také nastavte „version false
“, pak bude Grails/GORM/Hibernate stále zmatený a pokusí se vydat AKTUALIZACI, nikoli INSERT.
Pokud používáte automaticky generované ovladače Grails Scaffolding, pak je bezpečné použít "insert: true
" v ovladači "save()
", protože tato funkce se volá pouze při prvním ukládání nového objektu. Když uživatel upraví existující objekt, "update()
Controlleru Místo toho se používá funkce ". Pokud však někde děláte svou vlastní věc ve svém vlastním kódu... bude důležité zkontrolovat, zda doménový objekt již není v databázi, než vytvoříte ".save()
" a předejte pouze "insert: true
", pokud se skutečně jedná o první vložení.
Problém č. 4:Použití přirozených klíčů s Grails/GORM/Hibernate
Jedna poznámka na závěr, která se netýká hodnot Oracle GUID, ale souvisí s těmito problémy Grails obecně. Řekněme, že ve starší databázi (jako je ta, se kterou jsem se zabýval), některé z vašich tabulek používají jako primární klíč přirozený klíč. Řekněme, že máte OWNER_TYPE
tabulka obsahující všechny možné "typy" OWNER
a NAME
sloupec je jak pro člověka čitelný identifikátor, tak i primární klíč.
Aby to fungovalo s Grails Scaffolding, budete muset udělat několik dalších věcí. Za prvé, automaticky generované pohledy nezobrazují pole ID na obrazovce, když uživatelé vytvářejí nové objekty. Chcete-li přidat pole pro ID, budete muset do příslušného pohledu vložit nějaké HTML. Pokud dáte poli název "id
", pak funkce "save()" automaticky vygenerovaného ovladače obdrží tuto hodnotu jako "params.id
".
Zadruhé se musíte ujistit, že automaticky generovaný ovladač "save()
Funkce " správně vloží hodnotu ID. Při prvním vygenerování se "save()" spustí vytvořením instance objektu domény z parametrů CGI předávaných View:
def ownerTypeInstance = new OwnerType.get( params )
To však nezpracovává pole ID, které jste přidali do zobrazení. Stále to budete muset nastavit ručně. Pokud jste v zobrazení dali poli HTML název "id
", pak bude k dispozici v "save()
" jako "params.id
." “:
...
ownerTypeInstance = new OwnerType()
ownerTypeInstance.id = params.id
// Proceed to the ".save()" step, making sure to pass "insert: true"
...
Kus dortu, co? Možná, že "Problém #5" je zjišťování, proč se podrobujete všem těm bolestem, spíše než jen ruční psaní rozhraní CRUD pomocí Spring Web MVC (nebo dokonce vanilla JSP) na prvním místě! :)