Design: So vermeiden Sie, dass die Abwärtskompatibilität aufgrund von Datenbankänderungen beeinträchtigt wird

8

Dies ist mein Szenario, ich habe diese Schnittstelle:

public interface hitTheDataBase
{
    public void insertMe(String [] values);
    public void modifyMe(String [] values);
    public DataTable selectMe();
}

Und ich habe diese zwei Klassen, die die Schnittstelle implementieren:

public Class hitSqlServer implements hitTheDatabase
{
    public void insertMe(String [] values)
    {
         executes insert into table_in_sqlServerBD (col1, col2) values(values[0], values[1])
    }
    public void modifyMe(String [] values)
    {
         executes update table_in_sqlServerBD set col1 = values[0], col2 =  values[1] where rowid = values[3]
    }

    public DataTable selectMe()
    {
         executes select col1, col2 from table_in_sqlServerBD
    }
}

public Class hitSqLite implements hitTheDatabase
{
    public void insertMe(String [] values)
    {
         executes insert into table_in_sqLite (col1, col2) values(values[0], values[1])
    }
    public void modifyMe(String [] values)
    {
         executes update table_in_sqlLite set col1 = values[0], col2 =  values[1] where rowid = values[3]
    }

    public DataTable selectMe()
    {
         executes select col1, col2 from table_in_sqLite
    }
}

Dies ist Teil einer Beta-App, die tatsächlich in Test- und Produktionsumgebungen (!) Ausgeführt wird. Sie wird jedoch aufgrund von Fehlerkorrekturen, die nicht mit den Datenbankoperationen zusammenhängen, regelmäßig aktualisiert. Die Updates werden einfach per Deinstallation und Neuinstallation vorgenommen.

Jetzt habe ich eine neue Anforderung für eine ganz bestimmte Eckensituation, bei der eine neue Spalte "col3" zur Tabelle hinzugefügt werden muss, und ich muss auch Werte in diese Spalte einfügen, auswählen und aktualisieren. Das Problem ist, dass ich die Abwärtskompatibilität mit vorhandenen Datenbanken, in denen die Software bereits ausgeführt wird, nicht beeinträchtigen möchte.

Ich habe darüber nachgedacht, eine dritte Klasse zu codieren, die die HitTheDataBase-Schnittstelle implementiert, eine Hilfsklasse, um zu überprüfen, ob "col3" vorhanden ist, und so etwas wie:

hitTheDataBase hitMe = !helperclass.col3Exists() ? new hitSqlServer() : new hitSqlServerWithCol3();

Ist das ein guter Ansatz? Es sieht für mich gut aus, außer weil ich den Code in den Klassen ändern muss, die diejenigen verwenden, die "die Datenbank getroffen haben". Außerdem muss ich ständig überprüfen, ob der Wert von col3 vorhanden ist, um ihn in der GUI anzuzeigen, und den Benutzer ihn ändern lassen.

Zerbrochenes Fenster
quelle
2
Wird dies immer noch ein guter Ansatz sein, wenn Sie dies noch 15 Mal tun müssen?
Dan Pichelman
@ DanPichelman Nein, Ben hat recht: Design Geruch :(
Broken_Window
Der Beispielcode ist so schlecht, dass ich scharf Luft holte, als ich ihn sah.
Graham
@ Abraham es ist ein schrecklicher Java / C # Seudocode, der auf diese Weise erstellt wurde, weil er keinen Propietary Code
preisgibt

Antworten:

11

Gibt es einen Grund, warum Sie bei der Bereitstellung Ihrer Softwareupdates auch Ihr Schema nicht aktualisieren konnten? Eine Änderung der Software, die eine Änderung des Datenbankschemas erfordert, impliziert, dass sich das Schema auf dem Zielsystem ändern sollte.

Die Abwärtskompatibilität mit älteren Versionen eines Datenbankschemas sollte normalerweise vermieden werden, und das Hacken Ihrer Datenzugriffsschicht zur Unterstützung mehrerer Schemaversionen fühlt sich wie ein Designgeruch an.

Eine sauberere Lösung besteht darin, sicherzustellen, dass Ihr Code immer mit der Version des Schemas ausgeführt wird, für das dieser Code geschrieben wurde. Dies erleichtert nicht nur das Schreiben des Codes und hält den Code sauberer, sondern erleichtert auch das Testen des Codes. Sie können Migrationsskripte als Teil Ihres Installations- / Deinstallationsprozesses für das Upgrade sowie das Rollback einschließen.

Enthält Ihr Schema irgendeine Versionstabelle? Wenn nicht, müssen Sie so schnell wie möglich eine Schemaversionstabelle hinzufügen. Die Schemaversionierung ist für Upgrades und Rollbacks von entscheidender Bedeutung.

Über einen längeren Zeitraum werden Sie wahrscheinlich viele Schema-Upgrade-Skripte haben, die während der Installation / Deinstallation in einer bestimmten Reihenfolge ausgeführt werden müssen. Ein Schema-Versionierungsmechanismus ist der Schlüssel, um sicherzustellen, dass Schema-Upgrades und Rollbacks reibungslos ausgeführt werden.

Wenn Sie jedoch nicht über einen Mechanismus verfügen, mit dem Sie Ihr Schema mit Ihrer Softwareversion Schritt halten können, kann Ihre Datenzugriffsschicht möglicherweise komplexer werden, da Sie mit einer zunehmenden Anzahl von "Hacks" konfrontiert sind, die rückwärts beibehalten werden müssen Kompatibilität; Jedes Mal, wenn Sie etwas in Ihrem Schema ändern, werden Sie mit einem ständig wachsenden Aufwand an Regressionstests belastet.

Ben Cottrell
quelle
1

Dies ist der Fall, wenn Ihr Datenbankschema nicht mit einer Anwendungsversion übereinstimmt. Für jede Anwendung, die den neuen col3-Code erhält, sollte die Datenbank zusammen mit dieser aktualisiert werden.

Wenn Sie sich die Mühe machen möchten, zu überprüfen, ob eine Spalte in einer Tabelle vorhanden ist, erstellen Sie sie einfach während des Updates auf die neuere Version.

JeffO
quelle
Sie haben Recht, App und BD-Schema müssen übereinstimmen. Ich habe in der Vergangenheit das Ding "Fehlende Spalten erstellen" gemacht. Dies funktioniert nur, wenn der Benutzer, den ich für die Datenbankverbindung verwende, über ausreichende Berechtigungen verfügt. Dies ist nicht immer der Fall.
Broken_Window
Dann muss der Systemadministrator das von Ihnen bereitgestellte Aktualisierungsskript ausführen.
RubberDuck
@ RubberDuck das ist die Lösung, die ich verwendet habe. Ich habe die Aktualisierungsskripte in die Installationsdateien aufgenommen und erklärt, wie die Datenbank im Benutzerhandbuch aktualisiert wird.
Broken_Window
1

Ich würde nein sagen.

Diese Art von [endloser] "wenn, aber vielleicht, außer, außer" -Logik wird Sie nur in den Wahnsinn treiben und, was vielleicht noch wichtiger ist, Ihre Anwendung verlangsamen, da alle diese "Überprüfungen" zur Laufzeit durchgeführt werden.

Ich würde vorschlagen, Ihre Schemaänderungen zu versionieren und diese Version [Nummer] irgendwo in der Datenbank zu speichern (als Teil Ihres Upgrade-Fortschritts).

Erstellen Sie für jede Datenbankversion eine Version Ihrer Datenzugriffsklasse.

Fragen Sie zur Laufzeit die Datenbank nach der Schemaversion ab und instanziieren Sie darauf basierend die "richtige" Klasse.

Phill W.
quelle
2
Vielleicht möchten Sie erwähnen, dass Ihr letzter Satz ein Verweis auf das Strategiemuster ist.
TMN