Artur Södler Software-Qualität

Umgang mit Datenbanken


Datenbanken sind Speicherplatz und Verwaltungssoftware zugleich für fast jede komplexere Software. Moderne Datenbanken bezeichnet man gerne als relational, transaktional oder SQL-Datenbank.

Die Datenbank soll zum Beispiel sicherstellen, dass Ihr Kontostand bei der Bank stets mit jeder Buchung mitgeführt wird. Jede Überweisung setzt voraus, dass einer Abbuchung beim Zahlenden immer auch eine Gutschrift beim Zahlungsempfänger entgegensteht. Es wäre schlimm, wenn das nicht so wäre.

Bestimmte Änderungen in den Daten müssen also stets eine unteilbare logische Einheit, eine Transaktion bilden. Die Änderungen werden entweder komplett durchgeführt, oder gar nicht. Und zwischendurch kann auch niemand die Änderungen teilweise sehen.

In Kombination mit der Forderung, dass Software modular aufgebaut sein soll, also dass man Software-Bausteine zusammensetzt, muss man auch komplette Transaktionen zusammensetzen können. Wenn Modul 1 seine Transaktion 1 durchführt, muss auch Modul 2 seine Transaktion 2 durchführen. Scheitert eines, wird auch das andere ungültig.

Arbeitet Ihre Software mit schachtelbaren Transaktionen?

create procedure Buchung (p_konto number, p_betrag number, p_text varchar) is
begin
   insert into buchungen (zeit, konto, betrag, text)
   values (sysdate, p_konto, p_betrag, p_text);

   update konten set kontostand = kontostand + p_betrag
   where konto = p_konto;

   commit;
end Buchung;

create procedure Umbuchung (von_konto number, nach_konto number, betrag number, text varchar) is
begin
   Buchung (von_konto, -betrag, text);
   Buchung (nach_konto, betrag, text);

   commit; -- oder wie???
end Umbuchung;

Versuchen Sie doch bitte einmal, die Steuerung der Transaktion so zu legen, dass sowohl die Prozedur "Buchung" als auch die Prozedur "Umbuchung" eine atomare Transaktion bewirkt. Es geht nicht. Denn keine noch so moderne Datenbank kennt schachtelbare Transaktionen. Damit befinden wir uns wieder in der Steinzeit der Informatik, im Jahr 1952, direkt vor der Erfindung des Unterprogramms.

Es läuft darauf hinaus, dass die innere Prozedur "Buchung" genau dann die Transaktion steuern muss, wenn nur sie allein aufgerufen wird. Steuert aber bereits "Umbuchung" die Transaktion, muss das Unterprogramm "Buchung" sich heraushalten. Sie benötigen einen Kontext, den Sie entweder in jedem Unterprogramm als zusätzlichen Parameter (unübersichtlich!) mitgeben, oder als globale Daten.

Die objektorientierte Programmierung unterstützt diese Aufgabe optimal:

extern CDbConnection DbConnection;

void Buchung (long Konto, double Betrag, const char *Text) {
   CDbCursor c;
   CTransaction t;
   c.Open (DbConnection);
   c ["konto"] = Konto;
   c ["betrag"] = Betrag;
   c ["text"] = Text;
   t.Begin (DbConnection);
   c.Execute (
      "insert into buchungen (zeit, konto, betrag, text) "
      "values (sysdate, :konto, :betrag, :text)");
   c.Execute (
      "update konten set kontostand = kontostand + :betrag "
      "where konto = :konto");
   t.End();
}

Im obigen Beispiel unsichtbar, nichtsdestotrotz wichtig: Der Aufruf des Destruktors t.~CTransaction() im Falle einer Exception.

Die Transaktion t hat folgende Aufgaben:

•    Zugriff auf den globalen Transaktions-Schachtelungs-Zähler. Der Zugriff wird über das Objekt kontrolliert. Individuelle Zugriffe haben fatale Folgen, falls sie fehlerhaft sind.
•    Zählen der Schachtelungs-Tiefe. Bei Begin() eins hoch, bei End() eins herunter. Nur die äußerste Transaktion darf wirken.
•    Bestätigen oder widerrufen der äußersten Transaktion. Die eigentliche Aufgabe: bestätigen bei t.End(), widerrufen bei t.~CTransaction().
•    Integritätsprüfung. Wurde eine innere Transaktion abgebrochen, darf die äußere nicht bestätigt werden. Ein doppeltes Begin() wird ebenso verhindert wie End() vor Begin().
•    Start eines Watchdog. Die Transaktion muss innerhalb vernünftiger Zeit beendet werden.

Weitere wichtige Punkte im Zusammenhang mit Datenbanken:

•    saubere Trennung zwischen Datenbank und Oberfläche, Fehlermeldungen gehören in die Oberfläche
•    automatische Wiederholung bei Deadlocks, Deadlock-Vermeidungsstrategien
•    Datenaustausch zwischen zwei Datenbanken, Datenaustausch zwischen einer Datenbank und einem nicht-transaktionalen System
•    Sinn und Unsinn redundanter Daten
•    SQL oder Intelligenz des Software-Entwicklers — was ist besser?
•    dynamisches SQL verglichen mit parametriertem SQL
 
Artur Södler Software Qualität