Transaktionen in Access

Die Durchführung von Aktualisierungen am Datenbestand wird umso fehleranfälliger, je mehr Schritten die jeweilige Aktion beinhaltet und je mehr Personen gleichzeitig auf den betroffenen Datenbestand oder Teilmengen davon zugreifen können. Transaktionen sind eine Technik, die in beiden Fällen für Sicherheit sorgen und Inkonsistenzen vermeinden helfen.

Wofür Transaktionen

Stellen Sie sich das einfache Beispiel einer Bank vor, in der ein Mitarbeiter einen Betrag von beispielsweise 100 Euro von einem Konto auf ein anderes umbucht. Dazu sind zwei Schritte erforderlich: Man muss dem ersten Konto die 100 Euro abziehen und diese dem zweiten Konto zuschreiben. Diese beiden Vorgänge müssen untrennbar miteinander verbunden sein; die Durchführung nur eines der Vorgänge würde zu einer Inkonsistenz führen. Entweder gibt es dann in der Gesamtbilanz irgendwo 100 Euro zuviel oder zuwenig – je nachdem, welcher Vorgang schiefgelaufen ist.

Also benötigt man eine Technik, die mehrere Vorgänge “sammelt” und entscheidet, ob die betreffenden änderungen durchzuführen sind oder verworfen werden.

Im Datenbankbereich lassen sich Operationen als Transaktion zusammenfassen. Mit einer bestimmten Anweisung legt man fest, wann die Transaktion beginnt. Sind alle zusammenhängenden Aktionen erledigt, beendet man die Transaktion mit einer weiteren Anweisung. Erst dann wirken sich die durchgeführten änderungen auf den Datenbestand aus. Es gibt noch eine weitere Möglichkeit, eine Transaktion zu beenden: Wenn nicht alle enthaltenen Aktionen erfolgreich durchgeführt werden konnten, bricht man die Transaktion ab. In dem Fall werden alle der bis dahin durchgeführten änderungen am Datenbestand verworfen.

Transaktionen in der Praxis

Das obige Beispiel setzt voraus, dass theoretisch die Möglichkeit besteht, dass eine der innerhalb der Transaktion durchgeführten Aktionen scheitern könnte – das kann durchaus passieren, wenn etwa das Konto, von dem der Betrag abgebucht werden soll, nicht die notwendige Deckung aufweist. Kann man nicht einfach vor dem Durchführen der beiden Vorgänge prüfen, ob die Voraussetzungen erfüllt sind und erst dann die entsprechenden Aktionen durchführen

Die Antwort lautet Jein. Wenn es sich bei dem System um eine Einzelplatzlösung handelt, wie es etwa bei einer Buchhaltungssoftware für Freiberufler der Fall sein dürfte (die sich in der Regel keinen Mitarbeiterstab für die Regelung ihrer Finanzen leisten), können sie locker auf die Verwendung von Transaktionen verzichten.

Wenn es sich aber tatsächlich um eine Software handelt, die in einer Bank oder ähnlichen Institutionen eingesetzt wird, greifen unter Umständen mehrere Leute gleichzeitig auf ein und dasselbe Konto zu. Damit diese Vorgänge sich nicht überlappen, sperrt der erste Vorgang die relevanten Tabellen oder Datensätze und gibt diese erst nach Beendigung der Transaktion wieder frei. Die verwendeten Sperrmechanismen hängen dabei von den aktuell für diese Datenbank festgelegten Einstellungen ab. Wenn Sie optimistische Sperrung anwenden, sperrt eine Transaktion die betroffenen Datensätze erst beim Abschließen der Transaktion; beim pessimistischen Sperren hingegen beginnt die Sperre sofort mit dem Bearbeiten der Daten.

Transaktionen und Aktionsabfragen

Transaktionen können sich auf eine oder mehrere Tabellen beziehen. Bereits eine Aktionsabfrage zum Erhöhen aller Preise in einer Artikel-Tabelle kann Gegenstand einer Transaktion werden, wenn mehrere Datensätze daran beteiligt sind. Sie müssen allerdings bestimmte Regeln beachten, um die in Access eingebauten Transaktionsmechanismen zu nutzen.

Zum Ausführen von Aktionsabfragen gibt es mehrere Möglichkeiten:

  • Ausführen der Abfrage per Menübefehl
  • Aufruf mit DoCmd.RunSQL
  • Aufruf mit CurrentDB.Execute

Die ersten beiden Varianten sind identisch; das DoCmd-Objekt verschafft dem Entwickler lediglich Zugriff auf die Menüelemente per VBA. Zu Beispielzwecken verwenden Sie eine Tabelle, die lediglich die beiden Felder ZahlID und Zahl enthält und unter dem Namen tblZahlen gespeichert ist.

Um Fehler während Transaktionen nachstellen zu können, stellen Sie die Feldgröße des Feldes Zahl auf Integer und die Gültigkeitsregel auf >0 ein – so kann dieses Feld nur positive Zahlen aufnehmen (s. Abb. 1). Fügen Sie dem Feld ein paar Datensätze wie beispielsweise in Abb. 2 hinzu.

pic001.png

Abb. 1: Beispieltabelle tblZahlen

pic002.png

Abb. 2: Beispielwerte in der Tabelle tblZahlen

Implizite Transaktion mit Aktionsabfragen und DoCmd.RunSQL

Rufen Sie dann im Direktfenster folgende Aktionsabfrage auf, die von den im Feld Zahl enthaltenen Werten die Zahl 100 subtrahiert:

DoCmd.RunSQL "UPDATE tblZahlen SET Zahl = Zahl - 100"

Mit der beschriebenen Beispielkonfiguration und obiger Anweisung sollten Sie eine Meldung wie in Abb. 3 erhalten (zuvor kommt – je nach der Einstellung der Access-Optionen – noch eine Abfrage, ob Sie die Aktionsabfrage überhaupt durchführen möchten).

pic003.png

Abb. 3: Bei Fehlern in Aktionsabfragen erscheint eine Meldung.

Die Meldung deutet darauf hin, dass Access zwar schon versucht hat, die Abfrage durchzuführen, aber dies noch nicht getan hat – sonst ließe sich diese nicht einfach rückgängig machen. Mit einem Klick auf Ja führen Sie alle Teile der Abfrage durch, die nicht zu einem Fehler führen, mit einem Klick auf Nein brechen Sie die Transaktion ab. Allerdings lösen Sie damit einen Fehler aus (s. Abb. 4), um den Sie sich mit einer entsprechenden Fehlerbehandlung kümmern müssen.

pic004.png

Abb. 4: Das Abbrechen einer mit DoCmd.RunSQL gestarteten Aktionsabfrage führt zu einem Fehler.

Keine implizite Transaktion mit der Execute-Methode

In einer professionellen Anwendung möchten Sie den Benutzer möglicherweise nicht mit so einer Meldung konfrontieren. Im besten Fall können Sie vorher abschätzen, welche Probleme beim Durchführen einer Aktionsabfrage auftreten können und legen direkt im Code entsprechende Maßnahmen fest.

Die Execute-Anweisung des Database– oder Connection-Objekts erleichtert dies.

Die in der folgenden Prozedur verwendete Anweisung führt alle in der Aktionsabfrage enthaltenen Teile aus, die keinen Fehler auslösen:

Public Sub Aktionsabfrage()
    Dim db As DAO.Database
    Set db = CurrentDb
    db.Execute "UPDATE tblZahlen SET Zahl = Zahl - 100"
    Set db = Nothing
End Sub

Das bringt Sie auch nicht viel weiter, da der Benutzer nicht erfährt, das nicht alle gewünschten Operationen komplett ausgeführt wurden. Die folgende Variante unterscheidet sich lediglich durch den Parameter dbFailOnError in der Execute-Anweisung von der vorherigen Version:

Public Sub AktionsabfrageMF()
    Dim db As DAO.Database
    Set db = CurrentDb
    db.Execute "UPDATE tblZahlen SET Zahl = Zahl - 100", dbFailOnError
    Set db = Nothing
End Sub

Der Einsatz des Parameters dbFailOnError sorgt dafür, dass Access bei Problemen mit der Aktionsabfrage entsprechende Fehlermeldungen ausgibt, wie das Beispiel aus Abb. 5 zeigt.

pic005.png

Abb. 5: Fehler beim Ausführen der Execute-Methode

Mit einer Fehlerbehandlung können Sie eigene Fehlermeldungen erzeugen und vor allem die Anwendung anschließend fortsetzen.

Aber ist die mit der Execute-Methode abgesetzte SQL-Anweisung auch eine implizite Transaktion Werden bereits geänderte Datensätze beim Auftreten eines Fehlers wieder zurückgesetzt Ein Test ergibt: Nein! Probieren Sie die obige Prozedur einmal mit den Daten aus Abb. 6 aus. Normalerweise sollte der erste Datensatz geändert werden, dann ein Fehler beim zweiten Datensatz auftreten, weil dessen Wert größer als 0 sein muss und schließlich die änderungen verworfen werden.

pic006.png

Abb. 6: Beispielkonfiguration zum Testen implizierter Transaktionen per Execute-Methode

Das Ergebnis sieht anders aus: Der Wert des ersten Datensatzes wird wie gewünscht geändert, aber der letzte Datensatz nicht – das heißt, dass diese scheinbare Transaktion nicht komplett durchgeführt, aber auch nicht rückgängig gemacht wurde.

Explizite Transaktionen mit DAO

Abhilfe schafft die Prozedur aus Listing 1. Diese Prozedur verwendet eine explizite Transaktion und bettet die per Execute-Anweisung abgesetzte Aktionsabfrage darin ein.

Listing 1: Beispiel für eine Transaktion mit einer Execute-Anweisung

Public Sub ExecuteMitTransaktion()
    Dim db As DAO.Database
    Dim wks As DAO.Workspace
    On Error Resume Next
    Set wks = DBEngine.Workspaces(0)
    wks.BeginTrans
    Set db = CurrentDb
    db.Execute "UPDATE tblZahlen SET Zahl = Zahl - 100", dbFailOnError
    If Err.Number = 0 Then
         wks.CommitTrans
        MsgBox "OK"
    Else
         wks.Rollback
        MsgBox "Abgebrochen"
    End If
    Set db = Nothing
    Set wks = Nothing
End Sub

Dazu erstellen Sie zunächst einen Verweis auf die aktuelle Sitzung, indem Sie mit der Objektvariablen wks den aktuellen Workspace referenzieren.

BeginTrans, Commit und Rollback sind Methoden des Workspace-Objekts:

  • BeginTrans: Startet eine Transaktion.
  • Rollback: Schließt die Transaktion ab und verwirft alle enthaltenen änderungen.
  • Commit: Schließt die Transaktion ab und übernimmt die änderungen.

Mit der BeginTrans-Methode starten Sie die Transaktion innerhalb des Workspace-Objekts. Die Execute-Methode führt die Aktionsabfrage durch und sorgt mit dem dbFailOnError-Parameter dafür, dass beim Auftreten eines Fehlers die Eigenschaften des Err-Objekts mit entsprechenden Werten bestückt werden. Anschließend lässt sich mit dem Ausdruck Err.Number = 0 leicht ermitteln, ob bei der Ausführung der Aktionsabfrage ein Fehler aufgetreten ist.

Ist das nicht der Fall, sorgt die Methode Commit des Workspace-Objekts dafür, dass die durch Execute-Anweisung verursachten änderungen gespeichert werden, anderenfalls kommt die Rollback-Methode zum Tragen und verwirft alle änderungen.

Ohne CommitTrans kein Speichern

Wenn der Gültigkeitsbereich des Workspace-Objekts verlassen wird, ohne dass die Transaktion durch CommitTrans abgeschlossen wurde, werden die änderungen automatisch verworfen.

Unterstützt ein Recordset Transaktionen

Die meisten ODBC-Datenquellen unterstützen Transaktionen. Wenn Sie sich nicht sicher sind, ob die Daten, die in einer Transaktion geändert werden sollen, dies auch unterstützen, können Sie dies mit der Eigenschaft Transactions herausfinden. Mit einer Datensatzgruppe, auf die eine Objektvariable namens rst verweist, funktioniert das folgendermaßen:

Debug.Print rst.Properties("Transactions")

Schachtelung von Transaktionen

Sie können Transaktionen auf bis zu fünf Ebenen schachteln. Dabei müssen die von außen nach innen gestartet und von innen nach außen abgeschlossen werden. Sie können also auch die in einer Transaktion durchgeführten und abgeschlossenen Datenoperationen wieder rückgängig machen, indem Sie diese in eine weitere Transaktion einschließen.

Unvorhergesehene Laufzeitfehler

Falls Sie einmal auf einen Fehler treffen, den Sie nicht einordnen können, liegt es vielleicht daran, dass die Festplatte keinen Platz zum Anlegen der temporären Dateien mehr bietet. Die während einer Transaktion anfallenden Daten werden in einer temporären Datei zwischengespeichert. Diese Datei befindet sich in dem in der Temp-Umgebungsvariablen angegebenen Verzeichnis.

Explizite Transaktionen mit ADO

Auch ADO bietet die Möglichkeit expliziter Transaktionen. Dabei gibt es rein äußerlich zwei Unterschiede:

  • Die Transaktion ist kein Bestandteil eines Workspace, sondern der aktuellen Verbindung (Connection).
  • Die Methode zum Verwerfen der innerhalb der Transaktion durchgeführten Datenbankoperationen heißt RollbackTrans und nicht wie unter DAO nur Rollback.

Eine Transaktion unter ADO sieht wie in dem Beispiel aus Listing 2 aus.

Listing 2: Beispiel für eine Transaktion per ADO

Public Sub TransaktionMitADO()
    Dim cnn As ADODB.Connection
    Dim rst As ADODB.Recordset
    Set cnn = CurrentProject.Connection
    Set rst = New ADODB.Recordset
    On Error GoTo TransaktionMitADO_Err
    cnn.BeginTrans
    rst.Open "SELECT * FROM tblZahlen", cnn, adOpenDynamic, adLockOptimistic
    Do While Not rst.EOF
        rst!Zahl = rst!Zahl - 1
        rst.Update
        rst.MoveNext
    Loop
    cnn.CommitTrans
    MsgBox "OK"
TransaktionMitADO_Exit:
    rst.Close
    cnn.Close
    Set rst = Nothing
    Set cnn = Nothing
Exit Sub
TransaktionMitADO_Err:
cnn.RollbackTrans
MsgBox "Abgebrochen"
Resume TransaktionMitADO_Exit
End Sub

Zusammenfassung und Ausblick

Transaktionen sind in manchen Fällen unverzichtbar – das beste Beispiel dafür sind Operationen mit Bankkonten.

Sie können aber auch für andere Zwecke eingesetzt werden – zum Beispiel zum komfortablen Einsatz von Formularen und Unterformularen, wie der Beitrag Undo in Formularen mit Unterformular zeigt (www.access-im-unternehmen.de/276).

Downloads zu diesem Beitrag

Enthaltene Beispieldateien:

Transaktionen.mdb

Download

Schreibe einen Kommentar