Announcement

Collapse
No announcement yet.

IBX / Transaktionen

Collapse
X
  • Filter
  • Time
  • Show
Clear All
new posts

  • IBX / Transaktionen

    Hallo,
    <br>D5, IBX 4.6, IB 5.xx
    <br>
    <br>ich suche die "Ultimative Methode" Transaktionen für IBX zu implementieren, ohne für jedes IBDataSet eine separate Procedure zu implementieren.
    <br>Folgendes habe ich mir überlegt:
    <br>-bei jedem IBDataSet wird bei AfterPost folgendes gemacht:
    <br>GCommit := True
    <br>COMMIT; <b>//1) hier sollten doch jetzt zuerst die Ereignisse Before Close und AfterClose aller DataSets verarbeitet werden oder?</b>
    <br>GCommit := False
    <br>darauf hin werden alle DataSets geschlossen mit folgender Eigenschaft:
    <br>-BeforeClose
    <br>if GCommit then
    <br> Zwischenspeicher[x] := GetDataSetID <b>//2) wie kann man das herausfinden?</b>
    <br>-AfterClose
    <br>if GCommit then
    <br> DataSet.Open
    <br> locate(..Zwischenspeicher[x] // die zuvor zwischen gespeicherte ID
    <br>
    <br>Wieso das ganze?
    <br>Nachdem ich mir hier (und in einigen Büchern) einiges durchgelesen habe
    mußte ich Feststellen, das immer für ein bestimmtes DataSet eine Procedure geschrieben wurde, um das mit den Commits zu regeln. Das ist jedoch sehr aufwendig vor allem bei sehr vielen Datasets und die Gefahr durch Unachsamkeit einen Fehler zu produzieren ist auch groß. Wer sich schonmal das Beispiel Projekt RedSys2 angesehen hat wird bei der Procedure TDM.DoOpenBeitrag feststellen, das sich hier doch permanent der Code zum Positionieren "wiederholt". Das muß man doch auch zusammenfassen können oder (siehe obigen Ansatz)?
    <br>
    <br>Das Ziel ist es ein AfterPost, BeforeClose und ein AfterClose für alle DataSets zu entwickeln.
    <br>
    <br>Zu 2)
    <br>Wie kann man aus einem IBDataSet Objekt die ID und deren Wert herausfinden?
    <br>
    <br>Das ganze ist beim Posten ja auch gar nicht mal so schlimm. Hier könnte man ja noch CommitRetaining nehmen. Jedoch der Client2 <b>muß</B> ein Commit machen, damit er die Änderungen von Client1 lesen kann. ein Simples Close und Open hat bei mir leider nicht geholfen. Das heißt um einen vernünftigen refresh zu machen z.B. wenn ein Form geöffnet wird muß man ein Commit machen. Nur dummer weise wirkt sich dieses Commit ja auch auf die DataSets in anderen offene Forms aus und da wäre eine Simple Methode, die für alle DataSet Gültigkeit hat ideal.
    <br>
    <br>Wenn jemand eine bessere Idee hat bitte melden!
    <br>Geht das (Transaktionen) vieleicht mit IBO Komponenten besser?
    <br>Danke!
    <br>
    <br>mfg
    <br>PS

  • #2
    Hallo,

    das Problem bei den univerell für alles passenden Lösungen besteht darin, dass dort das Prinzip des "kleinsten gemeinsamen Nenners" berücksichtigt werden muss. In meinem Projekt <i>RedSys2</i> taucht im Datenmodul der folgende Kommentarblock auf:
    <pre>
    {
    Das Fenster für die Bearbeitung der Beiträge zu einer Ausgabe einer
    Publikation wird geöffnet. Das Programm muss genau den Datensatz
    wieder aktivieren, der vom Bearbeiten beim letzten Aufruf ausgewählt
    wurde. Dazu verwendet diese Methode schnellere und effektivere
    Alternativen zur Locate-Methode (die jedoch ebenfalls genutzt werden
    könnte).
    }
    </pre>
    Wenn man sich die "universelle" IBX-Methode Locate einmal im Detail anschaut, müssen dort viele Sachen gemacht werden, die im Fall von RedSys2 gar nicht notwendig ist, da das Ergebnis bereits zur Compilierungszeit feststeht. Der eigene Locate-Ersatz ist zwar mit Tippaufwand verbunden, aber zur Laufzeit deutlich schneller als das universelle Locate. Der manuelle Aufwand besteht unter anderen darin, in einem privaten Objektfeld des Datenmoduls den Wert des Primärschlüssels zwischenzuspeichern, über den der eigene Locate-Ersatz diesen Datensatz sehr schnell wiederfinden kann:
    <pre>
    {
    Datensatzzeiger hat sich geändert. Wenn sich die Datenmenge nicht im
    Locate-Modus (d.h. Auffinden der Position des letzten Aufrufs) befindet,
    wird die aktuelle Position in eigenen Objektfeldern gesichert, damit beim
    nächsten Aufruf von »DoOpenBeitrag« diese Position wieder automatisch
    aktiviert werden kann.
    }

    procedure TDM.IBDataSetBeitragAfterScroll(DataSet: TDataSet);
    begin
    if not FLocateMode then
    FCurrentBeitragID := IBDataSetBeitragBEITRAGID.Value;
    end;
    </pre>
    Man könnte nun zwar für die einzelnen Spalten persistente TFields anlegen und dort in der Eigenschaft <b>ProviderFlags</b> das Flag <b>pfInKey</b> als Kennzeichner für den Primärschlüssel verwenden, aber dann muss das Programm zur Laufzeit diese Spalte immer wieder neu suchen (obwohl das Ergebnis bereits zur Compilierungszeit feststeht und man das Ganze von Hand fest zuweisen kann).

    Im Beispielprojekt RedSys2 werden auch verschiedene TIBTransaction-Instanzen verwendet, damit ein Commit <b>keine</b> Auswirkungen auf die offenen Datenmengen der anderen Komponenten hat (die der anderen TIBTransaction-Instanz zugeordnet wurden)

    Comment


    • #3
      Hallo Herr Kosch
      <br>
      <br>anscheinend habe ich immer den Transaktions-Eigenschaften zu wenig Aufmerksamkeit gewidmet. So habe ich immer alle möglichen Eigenschaften eingestellt, aber nie <b>Commited-Lesen</d>. Mit <b>Commited-Lesen</d> ist es anscheinend so, das alle Daten die Client1 erstellt/ändert/löscht und mit CommitRetaining bestätigt von Client2 geshen/bearbeitet werden können, sobald Client2 die Datenmenge erneut abfargt (z.B. DataSet.Close; DataSet.Open (so wie es ja auch bei anderen Datenbanksystemen ist(d.h. hier müssen keine Änderungen an der App vorgenommen werden)).
      <br>Deshalb habe ich beschlossen die obige Idee (Diskussion #1) zu verwerfen und habe nach einigen Tests folgendes zusammen gestellt:
      <pre>
      1)
      Die gesamte Applikation bekommt ein Transaktionsobjekt zugeordnet (IBTransaction1). Mit der Eigenschaft Commited-Lesen. (Nur in Ausnahmen kann man ja eine neue Transaktion öffnen, wenn es darum geht bei Fehlern ein Rollback machen zu können)
      2)
      Jedes DataSet verweißt in BeforeOpen auf die folgende Prozedur:
      procedure DM1.MyBeforeOpen(DataSet: TDataSet);
      begin
      if Not IBTransaction1.InTransaction Then
      IBTransaction1.StartTransaction;
      end;
      3)
      Jedes DataSet verweißt in AfterPost und in AfterDelete auf die folgende Prozedur:
      procedure DM1.MyAfterPost(DataSet: TDataSet);
      begin
      TIBDataSet(DataSet).Transaction.CommitRetaining;
      end;
      4)
      Beim Schließen des MainForm (MDI Application) wird folgende Procedure ausgeführt:
      procedure TMainForm.FormClose(Sender: TObject; var Action: TCloseAction);
      begin
      IBTransaction1.Commit;
      end;
      </pre>
      <br>Nach meinen Tests hat es den Anschein, daß wenn man die obigen vier Punkte beachtet, dann erhält man ein ähnliches Verhalten wie bei anderen Datenbanksystemen.
      <br>
      <br>F1) Was halten Sie (oder andere Leser) davon? Habe ich irgend etwas übersehen/falsch interpretiert? Ist das das Schema "F" was man beibehalten kann?
      <br>F2) Reicht die Ausführung von 4) einmalig nach ca. 8 Std. (Betriebsschluß) (je Client) um den "entstandenen Schaden" (Umgehung des Garbage Collector durch 3)) wieder zu bereinigen?
      <br>F3) Der beschriebene Performance-Abfall durch 3) wird der sich schon innerhalb von 8 Std. bemerkbar machen? (IB Datenbankentwicklung mit D S.348) "...und Transaktionen nur mit CommitRetaining beenden, macht sich das früher oder später in einem Performance-Abfall bemerkbar." "früher" oder "später" läst sich das in Stunden oder Tagen bemässen ;-)?
      <br>
      <br>Danke schon mal für die Antwort!
      <br>
      <br>mfg
      <br>p

      Comment


      • #4
        Hallo Herr Kosch
        <br>
        <br>anscheinend habe ich immer den Transaktions-Eigenschaften zu wenig Aufmerksamkeit gewidmet. So habe ich immer alle möglichen Eigenschaften eingestellt, aber nie <b>Commited-Lesen</b>. Mit <b>Commited-Lesen</b> ist es anscheinend so, das alle Daten die Client1 erstellt/ändert/löscht und mit CommitRetaining bestätigt von Client2 geshen/bearbeitet werden können, sobald Client2 die Datenmenge erneut abfargt (z.B. DataSet.Close; DataSet.Open (so wie es ja auch bei anderen Datenbanksystemen ist(d.h. hier müssen keine Änderungen an der App vorgenommen werden)).
        <br>Deshalb habe ich beschlossen die obige Idee (Diskussion #1) zu verwerfen und habe nach einigen Tests folgendes zusammen gestellt:
        <pre>
        1)
        Die gesamte Applikation bekommt ein Transaktionsobjekt zugeordnet (IBTransaction1). Mit der Eigenschaft Commited-Lesen. (Nur in Ausnahmen kann man ja eine neue Transaktion öffnen, wenn es darum geht bei Fehlern ein Rollback machen zu können)
        2)
        Jedes DataSet verweißt in BeforeOpen auf die folgende Prozedur:
        procedure DM1.MyBeforeOpen(DataSet: TDataSet);
        begin
        if Not IBTransaction1.InTransaction Then
        IBTransaction1.StartTransaction;
        end;
        3)
        Jedes DataSet verweißt in AfterPost und in AfterDelete auf die folgende Prozedur:
        procedure DM1.MyAfterPost(DataSet: TDataSet);
        begin
        TIBDataSet(DataSet).Transaction.CommitRetaining;
        end;
        4)
        Beim Schließen des MainForm (MDI Application / es wird viel mit Grids und gleichzeitig geöffneten Forms gearbeitet ggf. untypisch für C/S aber ...) wird folgende Procedure ausgeführt:
        procedure TMainForm.FormClose(Sender: TObject; var Action: TCloseAction);
        begin
        IBTransaction1.Commit;
        end;
        </pre>
        <br>Nach meinen Tests hat es den Anschein, daß wenn man die obigen vier Punkte beachtet, dann erhält man ein ähnliches Verhalten wie bei anderen Datenbanksystemen.
        <br>
        <br>F1) Was halten Sie (oder andere Leser) davon? Habe ich irgend etwas übersehen/falsch interpretiert? Ist das das Schema "F" was man beibehalten kann?
        <br>F2) Reicht die Ausführung von 4) einmalig nach ca. 8 Std. (Betriebsschluß) (je Client) um den "entstandenen Schaden" (Umgehung des Garbage Collector durch 3)) wieder zu bereinigen?
        <br>F3) Der beschriebene Performance-Abfall durch 3) wird der sich schon innerhalb von 8 Std. bemerkbar machen? (IB Datenbankentwicklung mit D S.348) "...und Transaktionen nur mit CommitRetaining beenden, macht sich das früher oder später in einem Performance-Abfall bemerkbar." "früher" oder "später" läst sich das in Stunden oder Tagen bemässen ;-)?
        <br>
        <br>Danke schon mal für die Antwort!
        <br>
        <br>mfg
        <br>p

        Comment


        • #5
          Hallo Herr Kosch
          <br>
          <br>anscheinend habe ich immer den Transaktions-Eigenschaften zu wenig Aufmerksamkeit gewidmet. So habe ich immer alle möglichen Eigenschaften eingestellt, aber nie <b>Commited-Lesen</b>. Mit <b>Commited-Lesen</b> ist es anscheinend so, das alle Daten die Client1 erstellt/ändert/löscht und mit CommitRetaining bestätigt von Client2 geshen/bearbeitet werden können, sobald Client2 die Datenmenge erneut abfargt (z.B. DataSet.Close; DataSet.Open (so wie es ja auch bei anderen Datenbanksystemen ist(d.h. hier müssen keine Änderungen an der App vorgenommen werden)).
          <br>Deshalb habe ich beschlossen die obige Idee (Diskussion #1) zu verwerfen und habe nach einigen Tests folgendes zusammen gestellt:
          <br>1)
          <br>Die gesamte Applikation bekommt ein Transaktionsobjekt zugeordnet (IBTransaction1). Mit der Eigenschaft Commited-Lesen. (Nur in Ausnahmen kann man ja eine neue Transaktion öffnen, wenn es darum geht bei Fehlern ein Rollback machen zu können)
          <br>2)
          <br>Jedes DataSet verweißt in BeforeOpen auf die folgende Prozedur:
          <br>procedure DM1.MyBeforeOpen(DataSet: TDataSet);
          <br>begin
          <br> if Not IBTransaction1.InTransaction Then
          <br> IBTransaction1.StartTransaction;
          <br>end;
          <br>3)
          <br>Jedes DataSet verweißt in AfterPost und in AfterDelete auf die folgende Prozedur:
          <br>procedure DM1.MyAfterPost(DataSet: TDataSet);
          <br>begin
          <br> TIBDataSet(DataSet).Transaction.CommitRetaining;
          <br>end;
          <br>4)
          <br>Beim Schließen des MainForm (MDI Application / es wird viel mit Grids und gleichzeitig geöffneten Forms gearbeitet ggf. untypisch für C/S aber ...) wird folgende Procedure ausgeführt:
          <br>procedure TMainForm.FormClose(Sender: TObject; var Action: TCloseAction);
          <br>begin
          <br> IBTransaction1.Commit;
          <br>end;
          <br>Nach meinen Tests hat es den Anschein, daß wenn man die obigen vier Punkte beachtet, dann erhält man ein ähnliches Verhalten wie bei anderen Datenbanksystemen.
          <br>
          <br>F1) Was halten Sie (oder andere Leser) davon? Habe ich irgend etwas übersehen/falsch interpretiert? Ist das das Schema "F" was man beibehalten kann?
          <br>F2) Reicht die Ausführung von 4) einmalig nach ca. 8 Std. (Betriebsschluß) (je Client) um den "entstandenen Schaden" (Umgehung des Garbage Collector durch 3)) wieder zu bereinigen?
          <br>F3) Der beschriebene Performance-Abfall durch 3) wird der sich schon innerhalb von 8 Std. bemerkbar machen? (IB Datenbankentwicklung mit D S.348) "...und Transaktionen nur mit CommitRetaining beenden, macht sich das früher oder später in einem Performance-Abfall bemerkbar." "früher" oder "später" läst sich das in Stunden oder Tagen bemässen ;-)?
          <br>
          <br>Danke schon mal für die Antwort!
          <br>
          <br>mfg
          <br>p

          Comment


          • #6
            Hallo,

            der Isolationsgrad READ COMMITTED (Committed-Lesen) wird im SQL-Standard nur als Kompromiss betrachtet, da er gegen eine der Transaktionsregeln verstösst (innerhalb der eigenen, laufenden Transaktion sollte der Datenbestand immer so zu sehen sein, wie er beim Start der Transaktion war). Somit ist für puristische SQL-Fans anstelle von READ COMMITTED der Level REPEATABLE READ (alias SNAPSHOT beim InterBase) die 1. Wahl, denn nur hier werden die Anforderungen aus dem Standard erfüllt. Der InterBase muss intern bei READ COMMITTED mehr arbeiten als bei SNAPSHOT, so dass es ein Abwägung zwischen Aufwand und Nutzen ist.

            Die einzelnen Detailfragen lassen sich nur durch einen praktischen Test beantworten. Es hängt von vielen Faktoren ab (Seitengrösse der Datenbank, Tabellenstruktur, Anzahl der Datensätze in den Tabellen, Anteil von DELETEs und UPDATEs zu INSERT usw.), so dass es keine universelle Formel gibt, die für alle Einsatzfällt gilt.

            P.S: Was ist mit "anderen Datenbanksystemen" gemeint? Vermutlich ACCESS, dBASE oder Paradox - aber das sind alles Desktop-Datenbanken, die als datensatzorientierte Datenbanken nach einem völlig anderen Prinzip arbeiten als die mengenorientierten SQL-Datenbanken

            Comment

            Working...
            X