Datenbank-API in Groovy verwenden

Dieser Beitrag zeigt, wie im Groovy-Skript in Prozessen Datenbankoperationen mit der Intrexx Datenbank-API implementiert werden.

Vorteile des Intrexx APIs

Das Intrexx-API verfügt über eine Vielzahl an nützlichen Funktionen, um das Arbeiten mit Datenbank-Operationen in Intrexx zu erleichtern. So existiert z.B. eine Client-SQL-Funktion "DATAGROUP", die es erlaubt, in SQL-Statements anstelle des Namens einer Datengruppe deren GUID anzugeben.

Beispiel

g_dbQuery.executeAndGetScalarValue(conn, "SELECT COUNT(LID) FROM DATAGROUP('DAF7CECF66481FCABE50E529828116EAFE906962')")

Statt des Tabellennamens wird hier DATAGROUP('GUID_DATENGRUPPE') eingesetzt. Auf diese Weise können hartcodierte Datengruppennamen vermieden werden. Darüber hinaus wird eine korrekte Funktionsweise auch dann sichergestellt, wenn Applikationen und Prozesse mehrfach importiert werden, da bei einem Mehrfachimport innerhalb von Intrexx vorkommende GUIDs ersetzt werden. Ein Ersetzen von Namen ist jedoch nicht möglich.

Ein weiterer Vorteil der Verwendung des APIs von Intrexx ist es, dass im Vergleich zum Standard Java- oder Groovy-API weniger Code benötigt wird, um Datenbankabfragen und -operationen in Intrexx zu realisieren.

Beispiel

def iMax = g_dbQuery.executeAndGetScalarValue(g_dbConnections.systemConnection, "SELECT MAX(LID) FROM DATAGROUP('DAF7CECF66481FCABE50E529828116EAFE906962')", 0 )

Mit Hilfe dieser einen Zeile ist es möglich, den Maximalwert der ID einer Tabelle zu bestimmen und gleichzeitig mit dem Parameter 0 einen Fallbackvalue anzugeben, für den Fall, dass kein Datensatz gefunden wurde und somit "null" als Ergebnis zurückkommt.

Groovy-Skripts erhalten durch die Verwendung des Intrexx Datenbank-API eine höhere Robustheit. Beispielsweise resultieren Überläufe von Zahlenbereichen in einer ArithmeticException anstatt in einer neuen, unvorhersehbaren Wertzuweisung an die Variable. Außerdem befindet man sich durch die Verwendung des Intrexx-API zu jedem Zeitpunkt im Intrexx-Umfeld (z.B. bzgl. Transaktionssicherheit und Rechtemanagement).

Erzeugen und Schließen von Prepared Statements und Result Sets

Beim Erzeugen von Prepared Statements in Verbindung mit Schleifen sollte immer darauf geachtet werden, die Statements vor der Schleife zu erzeugen, innerhalb der Schleife zu füllen und auszuführen und anschließend außerhalb der Schleife wieder zu schließen. Auf diese Weise werden Speicherüberläufe vermieden und eine höhere Performance wird erzielt.

Falsch

for (i in 1..1000)
{
    def stmt = g_dbQuery.prepare(conn, "UPDATE DATAGROUP('DAF7CECF66481FCABE50E529828116EAFE906962') SET TEXT = ? WHERE LID = ?")

    stmt.setString(1, "Nummer ${i}")
    stmt.setInt(2, i)
    stmt.executeUpdate()
    stmt.close()
}

Richtig

def stmt = g_dbQuery.prepare(conn, "UPDATE DATAGROUP('DAF7CECF66481FCABE50E529828116EAFE906962') SET TEXT = ? WHERE LID = ?")

for (i in 1..1000)
{
    stmt.setString(1, "Nummer ${i}")
    stmt.setInt(2, i)
    stmt.executeUpdate()
}
stmt.close()

Allgemein sollte darauf geachtet werden, den close()-Befehl so früh wie möglich aufzurufen. Schließen Sie Statements oder Result Sets am besten immer, sobald diese abgearbeitet wurden und nicht mehr benötigt werden und warten Sie damit nicht bis zum Ende des Skripts. Es ist jedoch zu beachten, dass close() nur bei Prepared Statement und Result Set-Objekten aufgerufen werden muss. Werden Closures wie beispielsweise

g_dbQuery.executeUpdate(conn, "UPDATE DATAGROUP('DAF7CECF66481FCABE50E529828116EAFE906962') SET TEXT = ? WHERE LID = ?")
{
        setString(1, "Hello World")
        setInt(2, 1)
}

eingesetzt, muss und kann kein close() aufgerufen werden, da man an dieser Stelle über kein entsprechendes Objekt verfügt, das geschlossen werden müsste. In diesem Fall übernimmt Intrexx die notwendige Ressourcenverwaltung und -freigabe.

Iterieren über Result Sets

Beim Iterieren über ein Result Set ist auf die korrekte Abarbeitung des Result Sets zu achten. Darüber hinaus sollten typisierte Methoden wie getBooleanValue(index i) verwendet werden, um den Datentyp der Rückgabe genau zu spezifizieren. Somit können datenbankspezifische Unterschiede bei der Abbildung von Daten und den verwendeten Datentypen vermieden werden. Folgender Code ist nicht korrekt und führt zu Fehlern während der Iteration:

rs.each {
    def strVal1 = rs.getStringValue(1)
    def iVal2   = rs.getIntValue(2)
    def bVal3   = rs.getBooleanValue(3)
}

Stattdessen sollte eine der folgenden Varianten verwendet werden, um eine korrekte Iteration zu gewährleisten. Beispiel: Direkte Verarbeitung des Result Sets

while(rs.next())
{
    def strVal1 = rs.getStringValue(1)
    def iVal2   = rs.getIntValue(2)
    def bVal3   = rs.getBooleanValue(3)
}
Beispiel: Verarbeitung einzelner Zeilen eines Result Sets

rs.each {row ->
    def strVal1 = row.getStringValue(1)
    def iVal2   = row.getIntValue(2)
    def bVal3   = row.getBooleanValue(3)

}