Verknüpfte Datenbanken updaten

Wer Software für einen Kunden entwickelt und nicht ständig an der Produktiv-Version arbeitet, steht irgendwann vor der Anforderung, die Produktiv-Version gegen die neue Version auszutauschen. Dieser Beitrag zeigt eine Möglichkeit, ein solches Update ohne Reibungsverluste durchzuführen.

Zum Update einer Datenbank können mehrere Faktoren gehören. Es kann sein, dass man nur einen kleinen Fehler im Code behoben hat und dem Benutzer die neue Version zukommen lassen möchte – kein Problem, man tauscht einfach das am besten in einer Frontend-Backend-Konfiguration enthaltene Frontend aus und verknüpft das neue Frontend mit den Tabellen des alten Backends.

Oder man stellt fest, dass man ein wichtiges Feld im Datenmodell vergessen hat. Das wird schon schwieriger: Das Backend mit dem Datenmodell kann man nicht so schnell austauschen, denn es enthält ja die aktuell benutzten Daten. In dem Fall wird man am einfachsten zum Kunden fahren und das Datenmodell vor Ort ändern oder dies per Fernwartung erledigen. Ist beides nicht möglich, leitet man den Kunden durch diesen Vorgang, was erfahrungsgemäß je nach dessen Fertigkeiten kompliziert werden kann.

Sobald jedoch mehr als ein Kunde mit derselben Datenbank arbeitet oder häufige oder viele änderungen vorgenommen werden sollen, fällt die oben beschriebene Vorgehensweise flach.

Vielleicht treffen ja auch änderungen am Frontend und am Backend zusammen, was zumindest dann der Regelfall ist, wenn das Backend betroffen ist – änderungen am Frontend sind dann nicht ganz unwahrscheinlich.

Diese Kombination trifft sich gut, denn wenn man dem Kunden ein neues Frontend mit geänderter Benutzeroberfläche oder Anwendungslogik verpasst, kann man änderungen am Datenmodell gleich durch diese neue Version des Frontends erledigen lassen.

Genau wie einige andere Vorgänge, die normalerweise beim Öffnen der Datenbank ablaufen – also etwa das Prüfen des Backends und der Verknüpfungen zu den Tabellen im Backend oder die Konsistenzprüfung bestehender Daten -, können Sie hier auch änderungen am Datenmodell des Backends durchführen lassen.

Von A nach B

Wenn Sie eine Datenbankanwendung nur für einen Kunden erstellt haben und weiterentwickeln, liegt eine komfortable Situation vor. Ausgehend davon, dass der Kunde neue Versionen immer direkt einsetzt, brauchen Sie beim Start des neuen Frontends nur ein paar Codezeilen aufzurufen, welche die seit dem letzten Update angefallenen änderungen am Datenmodell durchführen.

Jedes weitere Update geht dann vom jeweils durch die Vorversion hergestellten Zustand aus und passt das Backend seinerseits an.

Ganz anders sieht es aus, wenn zwei oder mehr Benutzer die Dienste Ihrer Datenbank in Anspruch nehmen, wobei “mehrere Benutzer” nicht im Sinne von Mehrbenutzerumgebung, sondern von eigenständigen Anwendern mit eigenem Front- und Backend gemeint ist.

Nehmen wir an, es gäbe der Einfachheit halber nur zwei Benutzer, von denen der eine die Anwendung in Version 1.0 betreibt und der andere bereits ein Update auf die Version 1.1 vorgenommen hat – der erste Benutzer hat dies schlicht ausgelassen.

Wenn Sie nun eine neue Version 1.2 veröffentlichen, können Sie nicht mehr einfach nur davon ausgehen, dass diese Version überall die gleichen Voraussetzungen antrifft, um die in Version 1.2 enthaltenen änderungen etwa am Backend durchzuführen.

Eine Tabelle, die in Version 1.1 hinzugefügt wurde und in Version 1.2 um ein weiteres Feld ergänzt werden soll, wäre beim Update von Version 1.0 auf Version 1.2 noch gar nicht vorhanden und würde so zu einem Fehler beim Hinzufügen des Feldes zur nicht vorhandenen Tabelle führen.

Bevor jemand auf die Idee kommt: Es kommt natürlich nicht infrage, dass der Kunde nacheinander alle Zwischenversionen installiert, wenn er den Schritt von Version 1.0 etwa zu Version 1.6 machen möchte.

Sie müssen also zumindest eine Überprüfung einbauen, mit welcher Version die Anwender arbeiten, und selbst dafür sorgen, dass die änderungen der einzelnen Versionen inkrementell durchgeführt werden – auch wenn der Anwender gleich mehrere Versionen überspringt.

Verknüpft und zugenäht

Als kleines Bonbon kommt noch hinzu, dass neu im Backend angelegte Tabellen erst noch verknüpft und die Verknüpfungen geänderter Tabellen aktualisiert werden müssen.

Bevor Sie eine neue Version der Software verteilen, werden Sie vermutlich schon alle notwendigen Verknüpfungen angelegt haben, sodass diese beim Update nicht mehr hinzugefügt werden müssen (es wäre auch Unsinn, ein Frontend auszutauschen und dieses beim ersten Start nach dem Austausch dazu zu bringen, sich selbst anzupassen, indem es einige Verknüpfungen hinzufügt).

Normalerweise sollten Sie aufgeteilten Datenbanken aber ohnehin einen Mechanismus mitgeben, der jeweils beim Anwendungsstart prüft, ob das Backend an Ort und Stelle ist, und die Verknüpfungen aktualisiert.

Und da dieser doch recht eng mit dem Anlegen neuer Tabellen beim Update der Anwendung verknüpft ist, liefern wir diesen Mechanismus in diesem Beitrag gleich mit.

Aufbau

Die nachfolgend vorgestellten Techniken setzen ein paar Faktoren voraus. Zunächst einmal besteht das besprochene System wie erwähnt aus einer Frontend- und einer Backenddatenbank, wobei die Benutzeroberfläche (also Formulare, Berichte und Menüleisten), die Anwendungslogik und die Abfragen im Frontend und die durch die Anwendung zu verwaltenden Daten in entsprechenden Tabellen im Backend gespeichert sind.

Damit Sie im Frontend auf die in den Tabellen des Backends enthaltenen Daten genau so zugreifen können, als ob diese im Frontend enthalten wären, legen Sie im Frontend eine Reihe von Verknüpfungen an, die das Datenbankfenster (Access 2003) oder der Navigationsbereich (Access 2007) mit entsprechenden Symbolen ausgestattet im Bereich Tabellen aufführt (siehe Bild 1).

pic001.png

Bild 1: Verknüpfte Tabellen werden durch besondere Symbole markiert.

In dieser Abbildung sehen Sie gleich eine recht wichtige Tabelle, die entscheidend für die hier vorgestellte Technik ist: tbl_VersionBE. Diese Tabelle enthält nur ein Feld namens Version, das die Nummer der aktuellen Version speichert. In Bild 2 ist dies die Nummer 0.904. Wichtig: Natürlich muss sich diese Tabelle im Backend befinden und ist mit dem Frontend lediglich verknüpft! Anderenfalls würde diese ja mit jeder neuen Version des Frontends ausgetauscht.

pic002.png

Bild 2: Tabelle zum Speichern der Versionsnummer des Backends

Dem Frontend können Sie eine ähnliche Tabelle beispielsweise mit dem Namen tbl_VersionFE hinzufügen. Einen neuen Wert enthält die Tabelle mit jeder neuen Version des Frontends.

Es kann sehr nützlich sein, wenn Sie Informationen über die Version von Frontend und Backend an gut sichtbarer Stelle unterbringen, damit der Benutzer Ihnen diese bei Problemen gegebenenfalls leicht mitteilen kann; für die Fehlerbehandlung sind diese Informationen natürlich ebenfalls hochinteressant (vielleicht lassen Sie sich die Fehlermeldungen bei Laufzeitfehlern per E-Mail zusenden, dann sollten Informationen über die Version von Frontend und Backend nicht fehlen – neben Modul, Prozedurname, Zeile, Fehlernummer et cetera).

Ablauf

Bevor wir uns den Code ansehen, der für das Verknüpfen und Updaten des Backends sorgt (nicht vergessen: das Update des Frontends erfolgt ganz einfach durch seinen Austausch, das neue Frontend führt dann das Update des Backends aus!), schauen wir uns den Ablauf im Überblick an.

Diesen verschafft Abb. 3, die alle Schritte der im Modul mdlGlobal befindlichen Funktion StartDB der Beispieldatenbank darstellt, wobei die einzelnen Schritte durchnummeriert sind, damit wir uns im Text besser darauf beziehen können (auch in den folgenden Listings taucht diese Nummerierung auf).

Versionierung.emf

Bild 3: Ablauf beim Verknüpfen und Updaten des Backends durch ein neues Frontend

Im ersten Schritt (1) liest die Routine aus Listing 1 über die Hilfsfunktion GetCurrentBackendPath (s. Listing 2) den Pfad und den Dateinamen des Backends aus, auf den sich die im Frontend enthaltenen Tabellenverknüpfungen beziehen.

Listing 1: Die Routine StartDB steuert das Update des Backends und die Prüfung und Aktualisierung der Tabellenverknüpfungen.

Const cStrVersionTable = "tbl_VersionBE"
Public Function StartDB()
    Dim dbFrontend As DAO.Database, dbBackend As DAO.Database
    Dim strBackendpath As String
    Dim bolBackendFound As Boolean, bolTablesExist As Boolean, bolStartSuccessful As Boolean
    Dim strOldVersion As String, strNewVersion As String, strCurrentVersion As String
    Set dbFrontend = CurrentDb
    GetCurrentBackendPath dbFrontend, cStrVersionTable, strBackendpath
    On Error Resume Next
    bolBackendFound = Len(Dir(strBackendpath)) > 0
    On Error GoTo 0
    If bolBackendFound = False Then
        strBackendpath = dbFrontend.OpenRecordset("SELECT Optionvalue FROM tblOptions_Backend " _
        & "WHERE Optionname = 'Backendpath'").Fields(0)
        On Error Resume Next
        bolBackendFound = Len(Dir(strBackendpath)) > 0
        On Error GoTo 0
    End If
    Do While bolTablesExist = False
        Do While bolBackendFound = False
            strBackendpath = OpenFileName(Left(strBackendpath, InStrRev(strBackendpath, "\")), _
            "Backend auswählen", "Access 2007-Datenbank (*.accdb)|Access 2003 und älter-Datenbank " _
            & "(*.mdb)|Alle Dateien (*.*)")
            If Len(strBackendpath) = 0 Then Exit Function
            bolBackendFound = Len(Dir(strBackendpath)) > 0
        Loop
        Set dbBackend = OpenDatabase(strBackendpath)
        If CheckTables(dbFrontend, dbBackend, True) = True Then
            strOldVersion = dbBackend.OpenRecordset("SELECT Version FROM tbl_VersionBE").Fields(0)
            strNewVersion = dbFrontend.OpenRecordset("SELECT NeueVersion FROM tblVersionen " _
            & "ORDER BY ReihenfolgeID DESC").Fields(0)
            Do While Not strCurrentVersion = strNewVersion
                If UpdateVersion(dbFrontend, dbBackend, strOldVersion, strCurrentVersion) = True Then
                    strOldVersion = strCurrentVersion
                Else
                    MsgBox "Das Update wurde nicht durchgeführt."
                    Exit Function
                End If
            Loop
            bolTablesExist = CheckTables(dbFrontend, dbBackend, False) = True
        Else
            bolBackendFound = False
        End If
        If bolTablesExist = False Then
            If MsgBox("In der Datenbank '" & strBackendpath & "' fehlen eine oder mehrere der benötigten " _
            & "Tabellen." & vbCrLf & vbCrLf & "Klicken Sie auf 'OK', um eine andere Datenbank " _
            & "auszuwählen oder auf 'Abbrechen', um die Anwendung zu beenden.", _
            vbOKCancel + vbExclamation, "Falsche Datenbank") = vbCancel Then
            DoCmd.Quit
            Exit Function
        End If
    End If
Loop
If ReconnectBackend(dbFrontend, strBackendpath) = True Then
 dbFrontend.Execute "UPDATE tblOptions_Backend SET Optionvalue = '" & strBackendpath _
    & "' WHERE Optionname = 'Backendpath'", dbFailOnError
    bolStartSuccessful = True
End If
End Function

Listing 2: Die Funktion GetCurrentBackendPath ermittelt die Datenbank, mit der die Tabellenverknüpfungen aktuell verknüpft sind.

Public Function GetCurrentBackendPath(dbFrontend As _
    DAO.Database, strTable As String, _
    strBackendpath As String) As Boolean
    Dim strTemp As String
    strTemp = dbFrontend.TableDefs(strTable).connect
    strTemp = Mid(strTemp, InStr(1, strTemp, "DATABASE=") + 9)
    If InStr(1, strTemp, ";") > 0 Then
        strTemp = Left(strTemp, InStr(1, strTemp, ";") - 1)
    End If
    If Not Len(strTemp) = 0 Then
        strBackendpath = strTemp
        GetCurrentBackendPath = True
    End If
End Function

Dazu ist noch kein Kontakt mit dem Backend erforderlich; die Information wird per DAO über die Connect-Eigenschaft einer der Tabellenverknüpfungen ermittelt (siehe weiter hinten unter Aktuelle Backend-Datenbank ermitteln).

Hier gibt es zwei Möglichkeiten, die in (2) geprüft werden. Dabei testet die Routine mit der Dir-Funktion, ob die für die Verknüpfung angegebene Datei überhaupt existiert, und findet eines der folgenden beiden Ergebnisse vor:

  • Das Frontend wurde soeben als Update über das bestehende Frontend kopiert. Dann zeigen die Tabellenverknüpfungen noch auf die Backenddatenbank, die der Entwickler verwendet hat – es geht bei (3) weiter.
  • Das Update ist bereits erfolgt. Dann sind die Tabellenverknüpfungen bereits auf das Backend beim Benutzer eingestellt und eine erneute Auswahl des Backends entfällt. Möglicherweise stimmt der Pfad des Backends auf dem Entwicklersystem auch gleich mit dem Pfad beim Benutzer überein oder der Entwickler passt den Pfad vor der Weitergabe entsprechend an – in diesem Fall geht bei (4) weiter.

Wenn die in den Tabellenverknüpfungen angegebene Datenbankdatei nicht vorhanden ist, schaut die Routine in eine spezielle Tabelle namens tblOptions_Backend, die den Pfad des zuletzt verwendeten Backends speichert, aber auch durch den Entwickler auf einen Pfad voreingestellt werden kann (3). Dies bietet die Möglichkeit, ein Frontend, das auf mehreren Rechnern des Kunden installiert werden und auf das gleiche, auf einem Server liegende Backend zugreifen soll, direkt auf dieses einzustellen.

Im nächsten Schritt (5) prüft die Routine den Wert der Boolean-Variablen bolTablesExist, die erst den Wert True erhält, wenn alle Tabellen vorhanden und verknüpft sind – und dies ist beim Einstieg in die Routine noch nicht der Fall. Also geht es nun weiter zu (6), wo erneut eine Prüfung erfolgt, ob die zuvor ermittelte Datei überhaupt existiert. Ist das nicht der Fall, stimmt also auch der in der Tabelle tblOptions_Backend gespeicherte Wert nicht oder dieser ist leer. Dann muss der Benutzer in den sauren Apfel beißen und das Backend mit einem Datei öffnen-Dialog selbst auswählen (7). Dieses Spiel wiederholt sich so lange, bis der Benutzer eine vorhandene Datenbankdatei ausgewählt hat.

Ist dies geschehen, heißt dies noch lange nicht, dass er auch die richtige Datenbank gewählt hat – es kann ja auch irgendeine Access-Datei sein. Also prüft die Routine im nächsten Schritt (8), ob die gefundene Datenbank eine Tabelle namens tbl_VersionBE enthält, und liest deren Wert gegebenenfalls ein. Ist diese Tabelle nicht vorhanden, kann der Benutzer sich entscheiden, ob er ein anderes Backend auswählen (Schritte 9-10-11-5-6-7) oder die Anwendung beenden möchte (Schritte 9-10-11-12).

Ende des frei verfügbaren Teil. Wenn Du mehr lesen möchtest, hole Dir ...

den kompletten Artikel im PDF-Format mit Beispieldatenbank

diesen und alle anderen Artikel mit dem Jahresabo

Schreibe einen Kommentar