Oracle
 sql >> Datenbank >  >> RDS >> Oracle

Verwenden von Oracles GUID()-generierten IDs in Grails/Hibernate

Puh ... nach einem weiteren Tag des Ringens damit, glaube ich, ich habe meine Arme um das Ding gelegt. Diese Antwort deckt etwas mehr ab als die ursprüngliche Fragebeschreibung, aber das liegt daran, dass ich noch mehr Probleme gefunden habe, nachdem ich das Problem mit dem Hibernate-Generator gelöst hatte.

Problem Nr. 1:Erhalten einer Oracle GUID() Wert

Wie in der Antwort von Adam Hawkes beschrieben, wird der Hibernate-Generator „guid“ nicht gewartet und funktioniert nur für ältere Versionen des Oracle-Dialekts.

Wenn Sie jedoch den Hibernate-Generator „zugewiesen“ verwenden (was bedeutet, dass Sie Primärschlüssel manuell festlegen möchten, anstatt sie von Hibernate automatisch generieren zu lassen), können Sie Werte einfügen, die aus einem Oracle SYS_GUID() gezogen werden anrufen.

Obwohl die neueren Oracle-Dialekte von Hibernate "guid" nicht nahtlos unterstützen, verstehen sie dennoch die SQL, die zum Generieren dieser Werte erforderlich ist. Wenn Sie sich in einem Controller befinden, können Sie diese SQL-Abfrage wie folgt abrufen:

String guidSQL = grailsApplication.getMainContext().sessionFactory.getDialect().getSelectGUIDString()

Wenn Sie sich stattdessen innerhalb einer Domänenklasse befinden, können Sie dies immer noch tun ... aber Sie müssen zuerst eine Referenz auf grailsApplication einfügen. Wahrscheinlich möchten Sie dies jedoch in einem Controller tun ... mehr dazu weiter unten.

Wenn Sie neugierig sind, lautet der tatsächliche hier zurückgegebene String (für Oracle):

select rawtohex(sys_guid()) from dual

Sie können diese SQL ausführen und den generierten ID-Wert wie folgt abrufen:

String guid = grailsApplication.getMainContext().sessionFactory.currentSession.createSQLQuery(guidSQL).list().get(0)

Problem Nr. 2:Tatsächliche Verwendung dieses Werts in einem Grails-Domänenobjekt

Um diesen GUID-Wert tatsächlich in Ihrer Grails-Domänenklasse zu verwenden, müssen Sie den Hibernate-Generator "assigned" verwenden. Wie bereits erwähnt, bedeutet dies, dass Sie Ihre eigenen IDs manuell festlegen möchten, anstatt sie automatisch von Grails/GORM/Hibernate generieren zu lassen. Vergleichen Sie dieses modifizierte Code-Snippet mit dem in meiner ursprünglichen Frage oben:

...
static mapping = {
    table name: "OWNER"
    version false
    id column: "OWNER_OID", generator: "assigned"
    name column: "NAME"
    ...
}
...

In meiner Domänenklasse habe ich "guid" in "assigned" geändert. Ich habe auch festgestellt, dass ich die "columns {}" entfernen musste " Gruppierungsblock, und verschiebe alle meine Spalteninformationen um eine Ebene nach oben (seltsam).

Nun, in welchem ​​Controller auch immer diese Domänenobjekte erstellt werden ... Generieren Sie eine GUID wie oben beschrieben und fügen Sie sie in die "id" des Objekts ein "-Feld. In einem automatisch von Grails Scaffolding generierten Controller lautet die Funktion "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)
}

Sie könnten versuchen, diese Logik direkt in das Domänenobjekt einzufügen, in ein "beforeInsert() "-Funktion. Das wäre definitiv sauberer und eleganter, aber es gibt einige bekannte Fehler mit Grails, die verhindern, dass IDs in "beforeInsert() gesetzt werden " richtig. Leider müssen Sie diese Logik auf der Controller-Ebene beibehalten.

Problem Nr. 3:Sorgen Sie dafür, dass Grails/GORM/Hibernate dies richtig speichert

Die einfache Wahrheit ist, dass Grails hauptsächlich für brandneue Anwendungen gedacht ist und seine Unterstützung für ältere Datenbanken ziemlich lückenhaft ist (fairerweise ist es jedoch etwas weniger lückenhaft als andere "dynamische" Frameworks, die ich ausprobiert habe ). Selbst wenn Sie den "zugewiesen"-Generator verwenden, wird Grails manchmal verwirrt, wenn es darum geht, das Domänenobjekt zu speichern.

Ein solches Problem ist, dass ein ".save() " versucht manchmal, ein UPDATE durchzuführen, wenn es ein INSERT durchführen sollte. Beachten Sie, dass ich im obigen Controller-Snippet "insert: true hinzugefügt habe " als Parameter für ".save() " aufrufen. Dies weist Grails/GORM/Hibernate ausdrücklich an, eine INSERT-Operation anstelle einer UPDATE-Operation zu versuchen.

Alle Sterne und Planeten müssen ausgerichtet sein, damit dies richtig funktioniert. Wenn Ihre Domänenklasse "static mapping {} "-Block setzt den Hibernate-Generator nicht auf "assigned ", und setzen Sie außerdem "version false ", dann wird Grails/GORM/Hibernate immer noch verwirrt und versuchen, ein UPDATE anstelle eines INSERT auszugeben.

Wenn Sie automatisch generierte Grails Scaffolding-Controller verwenden, können Sie „insert: true verwenden " im "save()" des Controllers "-Funktion, da diese Funktion nur aufgerufen wird, wenn ein neues Objekt zum ersten Mal gespeichert wird. Wenn ein Benutzer ein vorhandenes Objekt bearbeitet, wird die "update() " Funktion wird stattdessen verwendet. Wenn Sie jedoch irgendwo Ihr eigenes Ding in Ihrem eigenen benutzerdefinierten Code machen ... ist es wichtig zu überprüfen, ob sich bereits ein Domänenobjekt in der Datenbank befindet, bevor Sie eine ".save() " aufrufen und nur "insert: true übergeben " Parameter, ob es sich wirklich um eine erstmalige Einfügung handelt.

Problem Nr. 4:Verwendung natürlicher Schlüssel mit Grails/GORM/Hibernate

Eine letzte Anmerkung, die nichts mit Oracle-GUID-Werten zu tun hat, sondern sich auf diese Grails-Probleme im Allgemeinen bezieht. Nehmen wir an, dass in einer Legacy-Datenbank (wie der, mit der ich mich befasse) einige Ihrer Tabellen einen natürlichen Schlüssel als Primärschlüssel verwenden. Angenommen, Sie haben einen OWNER_TYPE Tabelle, die alle möglichen "Typen" von OWNER enthält , und der NAME Spalte ist sowohl die für Menschen lesbare Kennung als auch der Primärschlüssel.

Sie müssen ein paar andere Dinge tun, damit dies mit Grails Scaffolding funktioniert. Zum einen zeigen die automatisch generierten Ansichten das ID-Feld nicht auf dem Bildschirm, wenn Benutzer neue Objekte erstellen. Sie müssen etwas HTML in die relevante Ansicht einfügen, um ein Feld für die ID hinzuzufügen. Wenn Sie dem Feld den Namen „id “, dann erhält die „save()“-Funktion des automatisch generierten Controllers diesen Wert als „params.id ".

Zweitens müssen Sie sicherstellen, dass der automatisch generierte Controller "save() " fügt den ID-Wert korrekt ein. Bei der ersten Generierung beginnt ein "save()" mit der Instanziierung eines Domänenobjekts aus den CGI-Parametern, die von View:

übergeben werden
def ownerTypeInstance = new OwnerType.get( params )

Dies behandelt jedoch nicht das ID-Feld, das Sie Ihrer Ansicht hinzugefügt haben. Sie müssen dies noch manuell einstellen. Wenn Sie in der Ansicht dem HTML-Feld den Namen „id ", dann ist es in "save() verfügbar " als "params.id ":

...
ownerTypeInstance = new OwnerType()
ownerTypeInstance.id = params.id 
// Proceed to the ".save()" step, making sure to pass "insert: true"
...

Ein Kinderspiel, oder? Vielleicht geht es in „Ausgabe Nr. 5“ darum, herauszufinden, warum Sie sich all diese Schmerzen auferlegen, anstatt Ihre CRUD-Schnittstelle überhaupt erst von Hand mit Spring Web MVC (oder sogar Vanilla-JSPs) zu schreiben! :)