Announcement

Collapse
No announcement yet.

Satzbearbeitung mit Firebird/Delphi7/IBO - wie?

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

  • Satzbearbeitung mit Firebird/Delphi7/IBO - wie?

    Ich mache momentan ein Redesign eines alten Turbopascal Multiuser Warenwirtschaftssystem unter D7, IBObjects und Firebird SQL Server.

    Ich habe aber ein großes Problem mit der Aktualisierung der Recordsets bei Änderungen durch andere User. Die nativen IBObjects Komponenten funktionieren soweit ausgezeichnet, sind schnell, kümmern sich um die Transaktionen etc. Sie emulieren auch ein Pessimistic Locking bei Editierung eines Datensatzes mit vorhergehender Syncronisierung.
    ABER (und hier liegen meine Probleme):
    1. Wie bilde ich Einzelsatz-Verarbeitung nach? D.h. es wird immer nur ein Datensatz in einem Formular angezeigt und eventuell bearbeitet. Der Anwender soll dann einfach vor/zurückblättern können bzw. durch einfache Suchfunktionen Datensätze finden.
    Die Suchfunktionen bzw. IB_Navigation funktionieren immer nur mit dem aktuellen Select Befehl und nicht auf die wirklich aktuellen Daten
    2. Wie aktualisiere ich den Select, um z.B. Änderungen anderen Anwender zu erfassen?

    Jedesfall ein Refresh auf "select * from Adressen" kann es ja nicht sein. Steht man mit seiner aktuell bearbeiteten Karte am Ende der aktuellen Sortierung wird jedesmal fast die komplette Tabelle aktualisiert und zum Client übertragen.

    Ich brauche doch aber nur den einen aktuellen Datensatz. Wenn der Anwender blättert nur den folgenden bzw. vorherigen. Und bei den Suchen-Funktionen nur den am Besten passenden Datensatz um hiernach eventuell vom Anwender vor/zurückblättern zu können.

    Ein paar Tips zu möglichen Lösungsansätzen wären sehr hilfreich für mich.

    Vielen Dank
    Gunther

  • #2
    Hallo Gunther,
    <br><br>
    bei solchen Dingen ist es immer etwas schwer, wo man anfangen soll, aber ich fang einfach mal an. ;-)
    <br><br>
    Ich würde die Suche und die Anzeige von Datensätzen in Listenform in einem "Übersichtsfenser" machen, wo ein Nur-Lese-Zugriff möglich ist, d.h. für diesen Zugriff kann eine eigene Transaktionskomponente mit "Read-Only" und Transaktionsisolationslebel "Read Committed" verwendet werden. In diesem Fenster ist auch eine Einschränkung der Datenmenge über Kriterien möglich. Wird dieses Fenster über einen Menüpunkt im Hauptformular aufgerufen, dann ist per Default die Datenmenge geschlossen, damit nicht gleich zu Beginn eine unnötige Anzahl an Datensätzen vom Server zum Client übertragen werden. Der Benutzer kann dann Filterkriterien definieren und mit einem Button anhand dieser Kriterien die Datenmenge einschränken. Wie gesagt, in diesem Formular passiert kein Editieren der Datensätze, sondern nur die Anzeige und Einschränkung der Datenmenge. Auf diesem Formular befindet sich auch ein IB_Navigator der das Browsen durch die Ergebnismenge ermöglicht.
    <br><br>
    Erst mit einem Doppelklick auf einen Datensatz in dieser Ergebnismenge kommt man zum eigentlichen Eingabeformular, wo auch der Datensatz editierbar ist. D.h. für dieses Formular würde ich dann eine Transaktionskomponente verwenden, die keine "Read-Only" Transaktionen verwendet. Auch auf diesem Formular befindet sich ein IB_Navigator, der allerdings auf die Datasource vom "Read-Only" Listenformular verweist. D.h. wenn man nun blättern will, wird im Hintergrund eigentlich in der Listendatenmenge navigiert und im AfterScroll-Event dieser Datenmenge wird dann der <b>eine</b> Datensatz im Eingabefeld mit einer erneuten Abfrage, die den Primarschlüsselwert des aktuellen Listendatensatzes in der WHERE-Klausel beinhaltet, ermittelt. Somit hat der Anwender die Möglichkeit in einer Datenmenge zu Navigieren, aber zum Editieren ist immer nur ein Datensatz abgeholt. Natürlich gehören Sonderfälle wie Einfügen eines Datensatzes behandelt. D.h. bei einem Insert-Modus im Eingabeformular sollte z.B. kein Navigieren möglich sein. Oder wenn ein Datensatz im Eingabeformular gelöscht wird, dann könnte man z.B. das Eingabeformular automatisch schliessen und man befindet sich automatisch wieder im Listenformular, ...
    <br><br>
    Was ich Dir auf jeden Fall ans Herz legen würde ist, dass mit "Form-Inheritance" arbeitest. Im Klartext bedeutet das, dass Du Dir "Basisformulare" entwirfst, die bereits so viel wie möglich an Logik enthält, und die speziellen Listen/Eingabe-Formulare dann von diesen "Basisformularen" abgeleitet sind. Somit erben diese Formulare alles was in den Basisformularen bereits implentiert wurde. D.h. hiermit kommt man sehr rasch zu Ergebnissen, und man muss nicht immer wieder alles von Grund auf neu machen.
    <br><br>
    Das oben erwähnte Design ist für eine echte Client/Server-Anwendung gut geeignet, da in der Regel wirklich nur Datensätze übertragen werden, die den Benutzer auch wirklich interessieren. Der Optimalfall ist mit dem Eingabeformular gegeben, weil Du immer nur genau einen Datensatz für ein mögliches Editieren abrufst. Diese ganze Sache gehört natürlich für Deinen speziellen Zweck noch verfeinert.
    <br><br>
    Naja, etwas viel "geschwafelt", aber vielleicht sind ein paar Denkanstöße dabei, die Dir weiterhelfen. Wichtig ist wirklich, dass Du erkennst, dass Du mit Firebird mit einem SQL Server und nicht mehr mit einer dateibasierten Desktop-Datenbank arbeitest. Ich würde zu Beginn viel Zeit in das Design (ein paar Anhaltspunkte hast Du oben schon bekommen) stecken, da man sich mit dem erwähnten "Form-Inheritance" für später <b>viel</b> Zeit und Mühen ersparen kann. Wenn dann noch nach definierten Regeln das Datenmodell erstellt ist, dann kann man in den Basisformularen noch mehr automatisieren! Ich denke hier z.B. an ein Primärschlüsselfeld, das ID heisst, oder <TABELLENNAME>_ID usw .... So kann man bereits das Füllen der TIB_Query Komponenten mit dem entsprechenden SQL-Statement in den Basisformularen erledigen. Wie gesagt: Zu Beginn viel Zeit in das Design stecken!
    <br><br>
    Wenn grammatikalische Fehler oder Rechtschreibfehler im obigen Text sind, dann einfach drüberlesen. Kommt vom schnellen Tippen. ;-)
    <br><br>
    Thoma
    Thomas Steinmaurer

    Firebird Foundation Committee Member
    Upscene Productions - Database Tools for Developers
    Mein Blog

    Comment


    • #3
      Thomas,
      erst mal vielen Dank für Deine umfangreiche Ausführung.
      1. Form-Inheritance: Benutze ich schon; wie Du schon erwähnst habe ich dort bereits große Teile der Logik, Menus, ActionLists etc. implementiert. In den Ableitungen (z.B. Adressen, Maschinenverwaltung, Lagerteile, etc.) erfolgt dann der Rest der Arbeit jeweils tabellenspezifisch. Funktionserweiterungen sind damit sofort auch in allen Ableitungen drin. Genial ist das!
      2. Im Datenmodell steckt schon einiges an Gehirmschmalz und ein primary Key Feld mit Generator ist in jeder Tabelle drin (heißt immer REF, so daß einige Logik damit auch im Basisformular überall in den Ableitungen funktioniert).
      3. ein solches übersichsfenster zur Grobsuche habe ich auch schon auf Sicht (ist in meiner alten DOS Applikation auch schon drin) ist aber noch nicht Implementiert.

      Aber ich habe da immer noch ein Aktualisierungsproblem in dem Übersichtsfenster im Multiuser-Betrieb. Die Anzeige friert ja quasi den Status während der Erstellung ein. Außerdem brauche ich für die Übersicht nur drei oder vier Felder, während das Editierungsformular alle Felder benötigt incl. zugehöriger Detail Tabellen.

      Was meinst Du von folgender Logik:
      *Übersichtsfenster zeigt nur die vier Hauptspalten an und läßt in einem Read-only Grid die freie Navigation und Suche zu; für die Editierung wird der PK (heißt hier REF) der Tabelle zurückgegeben.
      *in der Formularansicht (=Editierungsmodus) wird dann ein "select * from tabelle where REF=xx" ausgeführt welches nur einen Satz von der Datenbank zurück liefert und dann editierbar darstellt.

      Da mein Kunde auch auf die "blättern vorwärts/rückwärts" Navigation in der Formularansicht wert legt, kann ich das durchauch mit separaten SQLs nachbilden (z.B. "vorwärts blättern" select first(1) * from Adressen where ((suchname>=1) or (suchname=1 and REF>=2) order by SUCHNAME,REF")
      und als parameter p1 wird der aktuell angezeigte Name, P2 ist der aktuelle PK.
      Ich habe das schon getestet und es sieht funktionsfähig aus. Rückwärts blättern dann mit "<" zeichen und "order by suchname desc, ref desc". Dabei muß IBO dann zwar immer durch die PrepareSQL Phase und das blättern (speziell mit dann noch aktiven Detail Beziehungen) ist sehr viel zäher, als beim blättern durch eine größere zusammenhängendere Datenmenge eines "select * from adressen", aber es funktioniert.

      Auch für mich gilt: Wer Fehler findet, kann sie behalten. Tippe auch recht zügig und da schleicht sich mal was ein.

      Gunthe

      Comment


      • #4
        Hallo Gunther,
        <br><br>
        &gt; Aber ich habe da immer noch ein Aktualisierungsproblem in dem Übersichtsfenster im Multiuser-Betrieb. Die Anzeige friert ja quasi den Status während der Erstellung ein.
        <br><br>
        Natürlich. Du bekommst die Ergebnismenge, die Du zum Zeitpunkt des Ausführens der SQL-Anweisung anforderst. "Anfordern" würde einfach bedeuten, dass Du einen Button auf dem Übersichtsfenster hast, der Dir auf Klick die Ergebnismenge, die hinter dem Grid liegt, abhängig von den vorgefundenen Filterkriterien, öffnet. Die TIB_Transaction Komponente, die von dieser TIB_Query für das Grid verwendet wird, sollte Read-Only und im Transaktionsisolationslevel "Read Committed" inkl. AutoCommit sein. Jedes mal wenn man auf den Button klickt, wird die Ergebnismenge neu angefordert. Ich könnte mir vorstellen, dass Du den Transaktionsisolationlevel "Repeatable Read" (Consistency) verwendest. Hier bekommt man innerhalb einer Transaktion immer die selbe konsistente Ergebnismenge zurück. Erst wenn man eine neue Transaktion startet und die Ergebnismenge öffnet, bekommt man etwaige Änderungen mit. Solltest Du mit dem Aktualisierungsproblem etwas anderes meinen, dann müßtest Du mir noch mehr auf die Sprünge helfen.
        <br><br>
        &gt; Was meinst Du von folgender Logik:
        <br><br>
        Hört sich gut an, und so würde ich das auch machen.
        <br><br>
        Bzgl. Navigieren hast Du mich noch etwas falsch verstanden. Für das Übersichtsfenster wird es eine TIB_Query, TIB_Datasource und TIB_Navigator geben. Für das Eingabefenster existiert ebenfalls eine eigene TIB_Query, TIB_Datasource (damit Du etwaige Data-Aware Controls damit verbinden kannst) und TIB_Navigator. Allerdings verwendet TIB_Navigator im Eingabefenster als DataSource <b>nicht</b> die TIB_Datasource des Eingabefensters, sondern die TIB_Datasource vom Übersichtsfenster. D.h.: Navigierst Du mit TIB_Navigator im Eingabefenster, dann wird genau genommen in der TIB_Query des Übersichtsfensters navigiert. Das sieht man auch schön am Positionszeiger im Grid im Übersichtsfenster. Man muss dann halt nur sicherstellen, dass bei der Bewegung des Datensatzzeigers im Übersichtsfenster, der eine Datensatz im Eingabefenster aktualisiert wird. Aktualisieren heißt hier die TIB_Query im Eingabefenster mit dem Primärschlüsselwert des Datensatzes der TIB_Query im Übersichtsfenster neu öffnen. Somit wird im Hintergrund der korrespondierende Datensatz im Eingabefenster neu geladen. Dann geht das Spiel wieder von vorne los, usw ... Bei der TIB_Query im Eingabefenster sollte es sich um eine <b>parametrisierte Abfrage</b> handeln, wo dann nur der Primärschlüsselwert über ParamByName(...) entsprechend gesetzt wird. Hier kann man wieder vieles in das Basisformular hineingeben, wenn man eigene Namenskonventionen bzgl. Tabellen/Feld- und Parameternamen hat und einhält.
        <br><br>
        Bzgl. Übersichtsfenster noch ein kleiner Tipp. Es wäre eine Überlegung wert, dass für jedes Übersichtsfenster dahinter eine VIEW liegt. Dies hat als Vorteil, dass man ein SELECT * FROM machen kann, und nur die Felder zurückommen, die in der VIEW-Defintion vorhanden sind. Der zweite Vorteil ist, wenn man quasi Lookup-Felder (FOREIGN KEYs) in der Tabelle hat. Hier könnte man sich in der VIEW die Bezeichnung oder ähnliches aus der Lookup-Tabelle dazu joinen. Diese Bezeichnung ist dann in der VIEW voll als Feldname zugänglich und man kann dann auch ein ORDER BY darauf machen, wenn man dies einmal benötigt.
        <br><br>
        Sollte ich jetzt irgendeine Frage deinerseits übergangen haben, dann weise mich noch mal konkret darauf hin. ;-)
        <br><br>
        Ich hoffe das hilft Dir weiter.
        <br><br>
        Thoma
        Thomas Steinmaurer

        Firebird Foundation Committee Member
        Upscene Productions - Database Tools for Developers
        Mein Blog

        Comment


        • #5
          Hi Thomas,
          ja das sind schon einige gute Ideen.
          Ich denke ich werde mal quick und dirty eine kleine Testapplikation mit der Übersichts- und Eingabefunktion erstellen und die Funktionalitäten prüfen.
          <P>
          &gtJedes mal wenn man auf den Button klickt, wird die Ergebnismenge neu angefordert.&lt
          Also ein Refresh auf die IB_Query auslösen.
          <P>
          Der Tip mit der View ist gut!<p>

          Ich denke da kann ich erst mal etwas knobeln und testen.

          Danke für die Tipps

          Gunthe

          Comment

          Working...
          X