Home » Tutorials » Datenbanken » Interbase

Interbase

Was denn jetzt noch – oder: die Sache mit TIBTransaction

So, nach dem ersten Test wird hoffentlich wieder Ernüchterung eingetreten sein. So einfach ist es jetzt auch wieder nicht, eine SQL-DB Client zu schreiben. Nach dem Einfügen eines Datensatzes findet er sich nicht unten in unserer schönen Übersicht ein. Nach dem Beenden und Neustarten des Programms ist er auf einmal da! Das hängt schlicht an unserer IBTransaction.
Was hat es mit dieser Komponente auf sich? Wieder muss ich ein Stück in die Theorie abgleiten.

Was ist eine Transaktion?

Eine Transaktion kann man sich am besten mit folgendem Vorgang, der in jeder Bank abläuft, vorstellen: Durch eine Überweisung wird ein Girokonto belastet. Das Geld wird von dem Guthaben (ich nehme einfach mal an, da ist genug Geld drauf) abgezogen. Dann bricht der alte IBM-Server zusammen. Das Geld ist vom Konto abgebucht, aber noch nicht gutgeschrieben. Nach dem Reboot des Servers wird diese Transaktion aber wieder rückgängig gemacht, d.h. das Geld wird wieder dem Girokonto gutgeschrieben. Somit geht auch nix verloren.
Interbase arbeitet mit diesen Transaktionen, und was noch viel wichtiger ist: Interbase arbeitet mit dem sog. Multi-Generationen-Prinzip. Im Klartext heißt das:
Benutzer A greift auf die Tabelle Adresse zu. Dieser Zugriff läuft in einer Transaktion ab. Diese Transaktion bekommt die Nummer 2345 und wurde um 9:05:12,982 Uhr gestartet. Benutzer B greift um 9:09:32,234 Uhr ebenfalls auf die Tabelle Adresse zu. Er bekommt die Transaktionsnummer 2346. Da A sich gerade einen Kaffee holt und mit der neuen Sekretärin flirtet, bleibt bei ihm die Transaktion so stehen. Benutzer B ändert nun in der Tabelle Adresse einen Datensatz und schließt die Transaktion um 9:14:21,211 Uhr ab. Endlich merkt er, dass A mit der Neuen schäkert, und macht sich in die Küche auf und vertreibt Benutzer A. Dieser zieht sich, genervt von B, zurück und begibt sich mit seinem Kaffee an seinen Arbeitsplatz, wo er sich eine Adressliste erstellen lässt und diese ausdruckt. Jetzt kommt’s: Benutzer A bekommt die Daten in der Version von 9:05:12,982 und nicht die geänderten Daten! Dazu müsste er erst die Änderungen sozusagen vom Server nochmals anfordern, indem er die alte Transaktion schließt und eine neue Transaktion öffnet, die dadurch eine höhere Transaktionsnummer erhält als sie Benutzer B hatte. Der Interbase hält zu diesem Zeitpunkt sozusagen zwei unterschiedliche Datenbankversionen vor!
Was hat das jetzt für Auswirkungen für unser Programm? Folgenden Code:

procedure TFMain.SQLUpdate;
begin
  with DM do
  begin
    if IBTrans.InTransaction then
      IBTrans.Commit;
    IBAdressen.Open;
    IBTrans.StartTransaction;
    Grid.Open;
    if SelectAdress<>0 then
      Grid.Locate('ID',SelectAdress,[])
    else
    try
      selectAdress:=Grid['ID'];
    except
    end;
    Adresse.Close;
    Adresse.Params[0].AsInteger:=selectAdress;
    Adresse.Open;
  end;
end;

Die Prozedur sieht fast so aus wie der Inhalt von TFMain.OnShow? Richtig, deshalb können wir diesen auch löschen!
Was macht diese Prozedur? Einfach gesagt wird überprüft, ob eine Transaktion am Laufen ist. Wenn ja, wird sie mit Commit beendet und sofort wieder eine neue Transaktion geöffnet. Somit werden die Änderungen an den Daten in die DB geschrieben, damit sie von anderen gelesen werden können, und deren Änderungen werden von unserem Client erneut angefordert.
Damit diese aber auch wirklich funktioniert, muss diese Prozedur in der sbtnPostOnClick-Prozedur aufgerufen werden, nach dem Post und dem MaskeSetzen, sowie in der OnShow-Prozedur des Formulars. Der restliche Inhalt der OnShow-Methode wird gelöscht. Die Cancel-Methode muss ebenfalls verändert werden: Hier wird nach dem Cancel ein Rollback durchgeführt, d.h. alle bis dahin vorgenommenen Veränderungen werden verworfen.

 DM.Adresse.Cancel;
  DM.IBTrans.Rollback;
  MaskeSetzen;

Des Weiteren habe ich die Menge der Daten, die vom Server zum Client gesendet wird, drastisch eingeschränkt, denn es wird nur noch diese Adresse komplett übertragen, die im DBGrid selektiert ist. „selectAdress“ ist eine Variable, die ich ebenfalls im private-Teil als Integer deklariert habe. Im OnShow-Ereignis wird sie mit „0“ initialisiert, damit nichts passieren kann.
Die SelectSQL der Adresse-Komponente habe ich folgendermaßen geändert:

 SELECT * FROM adresse WHERE id=:AdressID

Damit funktioniert oben genannter Aufruf.
Als wirklich letzte Handlung musst du dafür sogen, dass bei einem Klick auf das DBGrid auch der Datensatz in der Detailmaske angezeigt wird. Dazu in das OnCellClick Ereigniss des DBGrid folgenden Code reinschreiben:

procedure TFMain.DBGrid1CellClick(Column: TColumn);
begin
  selectAdress:=DM.Grid['ID'];
  with DM do
  begin
    Adresse.Close;
    Adresse.Params[0].AsInteger:=selectAdress;
    Adresse.Open;
  end;
end;