4D v14.3

Einführung in Trigger

Home

 
4D v14.3
Einführung in Trigger

Einführung in Trigger  


 

 

Ein Trigger ist eine Methode, die einer Tabelle zugeordnet ist. Er ist eine Tabelleneigenschaft. Sie rufen Trigger nicht auf; die Engine der 4D Datenbank ruft sie automatisch auf, wenn Sie Datensätze der Tabelle bearbeiten, also Datensätze hinzufügen, löschen, ändern und laden. Sie können ganz einfache Trigger schreiben und sie dann komplexer machen.

Trigger sind ein leistungsstarkes Werkzeug, denn sie unterbinden “illegale” Operationen auf Datensätze Ihrer Datenbank. Sie lassen nur bestimmte Operationen für eine Tabelle zu und verhindern, dass Daten versehentlich verloren gehen oder beschädigt werden. Ein Trigger sorgt zum Beispiel in einem Rechnungssystem dafür, dass der Benutzer eine Rechnung nur hinzufügen kann, wenn er auch den Kunden einträgt, an den die Rechnung gestellt wird.

Erstellen Sie in der Designumgebung eine Tabelle, hat sie standardmäßig keinen Trigger.

Wollen Sie für eine Tabelle einen Trigger verwenden, müssen Sie:

  • Den Trigger aktivieren und 4D anweisen, dass er aufgerufen werden soll.
  • Den Code für den Trigger schreiben.

Aktivieren Sie einen Trigger, der noch nicht geschrieben wurde bzw. schreiben Sie einen Trigger, ohne ihn zu aktivieren, hat er keine Auswirkung auf die Operationen der Tabelle.

Wählen Sie dazu im Inspektorfenster der Struktur unter Trigger eine Option (Datenbankereignisse) für die Tabelle:

Mit dieser Option wird der Trigger immer aufgerufen, wenn in der Tabelle ein Datensatz geändert wird.
Dieser Fall tritt ein, wenn Sie:

Hinweis: Zur Optimierung wird der Trigger nicht aufgerufen, wenn der Datensatz vom Benutzer oder über den Befehl SAVE RECORD gesichert wird und in der Tabelle kein Feld im Datensatz geändert wurde. Wollen Sie den Aufruf des Triggers in diesem Fall erzwingen, können Sie einfach ein Feld auf sich selbst zuweisen:
 [thetable]thefield:=[thetable]thefield

Mit dieser Option wird der Trigger immer aufgerufen, wenn in der Tabelle ein Datensatz gelöscht wird.
Dieser Fall tritt ein, wenn Sie:

  • Einen Datensatz löschen (Designumgebung, über DELETE RECORD, DELETE SELECTION oder den SQL Befehl DELETE).
  • Eine Operation ausführen, die verknüpfte Datensätze über die Löschkontrollen einer Verknüpfung löscht.
  • Ein Plug-In einsetzen, das den Befehl DELETE RECORD aufruft.

Hinweis: Der Befehl TRUNCATE TABLE ruft keinen Trigger auf!

Wichtig: Führen Sie eine Operation bzw. einen Befehl für mehrere Datensätze aus, wird der Trigger für jeden Datensatz aufgerufen. Rufen Sie zum Beispiel den Befehl APPLY TO SELECTION für eine Tabelle auf mit einer aktuellen Auswahl von 100 Datensätzen, wird der Trigger 100 Mal aufgerufen.

Mit dieser Option wird der Trigger immer aufgerufen, wenn in der Tabelle ein Datensatz hinzugefügt wird.
Dieser Fall tritt ein, wenn Sie:

  • Einen Datensatz in Dateneingabe hinzufügen (Designumgebung, über den Befehl ADD RECORD oder den SQL Befehl INSERT).
  • Einen Datensatz mit CREATE RECORD und SAVE RECORD erstellen und sichern. Beachten Sie, dass der Trigger beim Aufrufen von SAVE RECORD und nicht beim Erstellen aktiv wird.
  • Datensätze importieren (Designumgebung oder über einen Importbefehl).
  • Andere Befehle aufrufen, die neue Datensätze erstellen und/oder sichern, z.B. ARRAY TO SELECTION, SAVE RELATED ONE, etc..
  • Ein Plug-In einsetzen, das die Befehle CREATE RECORD und SAVE RECORD aufruft.

Einen Trigger für eine Tabelle erstellen Sie entweder über das Fenster Explorer, oder Sie klicken im Inspektorfenster auf die Schaltfläche Bearbeiten oder doppelklicken bei gedrückter alt-Taste unter Windows bzw. Wahltaste auf Macintosh auf den Tabellentitel im Strukturfenster. Weitere Informationen dazu finden Sie im Abschnitt Trigger des Handbuchs 4D Designmodus.

Ein Trigger kann für eines der oben beschriebenen Datenbankereignisse ausgelöst werden. Mit der Funktion Trigger event prüfen Sie das Ereignis innerhalb des Triggers. Diese Funktion gibt für das jeweilige Datenbankereignis eine Zahl zurück.

Sie schreiben einen Trigger mit der Struktur Case of auf das von Trigger event zurückgegebene Ergebnis. Sie können die Konstanten unter dem Thema LAST SUBRECORD verwenden:

  //  Trigger für [beliebigeTabelle]
 C_LONGINT($0)
 $0:=0 // Eine Datenbankanfrage wird ausgelöst
 Case of
    :(Trigger event=On Saving New Record Event)
  // Geeignete Aktionen zum Sichern eines neu angelegten Datensatzes ausführen
    :(Trigger event=On Saving Existing Record Event)
  // Geeignete Aktionen zum Sichern eines bereits vorhandenen Datensatzes ausführen
    :(Trigger event=On Deleting Record Event)
  // Geeignete Aktionen zum Löschen eines Datensatzes ausführen
 End case

Ein Trigger hat zwei grundlegende Ziele:

  • Aktionen an einem Datensatz ausführen, bevor dieser gesichert oder gelöscht wird.
  • Eine Operation der Datenbank bestätigen oder zurückweisen.

Sie verwenden einen Trigger, um damit einen Datensatz zu prägen, sobald er gesichert oder geändert wird bzw. Informationen an eine andere Tabelle weitergibt. Der folgende Programmcode prägt z.B. einen Datensatz mit der Objektmethode Zeitstempel:

  // Trigger für Tabelle [Dokumente]
 Case of
    :(Trigger event=On Saving New Record Event)
       [Dokumente]Erstellt:=Time stamp
       [Dokumente]Geändert:=Time stamp
    :(Trigger event=On Saving Existing Record Event)
       [Dokumente]Geändert:=Time stamp
 End case

Hinweis: Time stamp gibt die Anzahl Sekunden zurück, die seit einem willkürlich festgelegten Datum vergangen sind.

Haben Sie diesen Trigger geschrieben und aktiviert, spielt es keine Rolle, ob Sie einen Datensatz über Dateneingabe, Import, eine Projektmethode oder ein 4D Plug-In hinzufügen bzw. ändern. Der Trigger prägt automatisch die Felder [Dokumente]Erstellt und [Dokumente]Geändert, bevor der Datensatz auf die Festplatte geschrieben wird.

Hinweis: Dieses Beispiel wird unter dem Befehl GET DOCUMENT PROPERTIES ausführlich beschrieben.

Um eine Datenbankoperation zu bestätigen oder zurückzuweisen, muss der Trigger im Funktionsergebnis $0 einen Trigger Fehlercode zurückgeben.

Wir gehen aus von einer Tabelle [Angestellte]. Dafür gilt die Regel, dass sämtliche Datensätze eine gültige Sozialversicherungsnummer enthalten müssen. Dafür weisen Sie dem Feld[Angestellte]SV Nummer die Objektmethode Gute SVNr zu. Diese überprüft die Eingaben der Sozialversicherungsnummer, wenn Sie auf die Schaltfläche Bestätigen klicken:

  // Objektmethode für Schaltfläche bAccept
 If(Good SS number([Employees]SS number))
    ACCEPT
 Else
    BEEP
    ALERT("Sozialversicherungsnummer eingeben und dann erneut auf OK klicken.")
 End if

Ist die Sozialversicherungsnummer korrekt, bestätigen Sie die Dateneingabe; ist sie falsch, erscheint eine Meldung. Sie bleiben in der Dateneingabe.

Sie können die Datensätze in [Employees] auch per Programmierung erstellen. Der folgende Teil eines Code wäre zwar von der Programmierung her gültig, würde aber gegen die Regel der vorigen Objektmethode verstoßen:

  // Auszug aus einer Projektmethode
  // ...
 CREATE RECORD([Employees])
 [Employees]Name:="DOE"
 SAVE RECORD([Employees]// <-- DB Verstoß gegen die Regel! Die SV Nummer wurde nicht zugewiesen!
  // ...

Mit einem Trigger für die Tabelle [Employees] können Sie die Regel [Employees]SS number auf allen Ebenen der Datenbank vorschreiben. Der Trigger sieht folgendermaßen aus:

  // Trigger für [Employees]
 $0:=0
 $dbEvent:=Trigger event
 Case of
    :(($dbEvent=On Saving New Record Event)|($dbEvent=On Saving Existing Record Event))
       If(Not(Good SS number([Employees]SS number)))
          $0:=-15050
       Else
  // ...
       End if
  // ...
 End case

Sobald dieser Trigger geschrieben und aktiviert ist, erzeugt die Zeile SAVE RECORD ([Employees]) den Fehler -15050 der Datenbank-Engine und der Datensatz wird NICHT gesichert.

Dasselbe gilt, wenn ein 4D Plug-In versucht, einen Datensatz [Employees] mit einer ungültigen Sozialversicherungsnummer zu sichern. Der Trigger erzeugt denselben Fehler und der Datensatz wird NICHT gesichert.

Der Trigger garantiert, dass niemand bewusst oder versehentlich eine ungültige Sozialversicherungsnummer eingeben und sichern kann.

Beachten Sie, dass Sie auch ohne Trigger auf eine Tabelle beim Sichern bzw. Löschen eines Datensatzes Fehler der Datenbank-Engine erhalten können. Versuchen Sie zum Beispiel, einen Datensatz mit doppelten Wert in einem einmaligen indizierten Feld zu sichern, erhalten Sie den Fehler -9998.

Trigger, die Fehler zurückgeben, fügen demnach in Ihrer Anwendung neue Fehler der Datenbank-Engine hinzu:

  • 4D verwaltet die “regulären” Fehler: Einmaliger Index, relationale Datenkontrolle, usw.
  • Über Trigger verwalten Sie eigene Fehler speziell in Ihrer Anwendung.

Wichtig: Sie können einen beliebigen Wert für den Fehlercode zurückgeben. Verwenden Sie jedoch KEINE Fehlercodes, die bereits für die 4D Datenbank-Engine reserviert sind. Sie sollten Nummern zwischen -32000 und -15000 verwenden.

Trigger-Fehler auf Prozessebene behandeln Sie genauso wie Fehler der Datenbank-Engine:

  • Sie können 4D das Standardfenster für Fehlermeldung anzeigen lassen, die Methode wird dann angehalten.
  • Sie können eine mit ON ERR CALL eingebaute Fehlerverwaltungsmethode verwenden und den Fehler in geeigneter Weise ausfindig machen.

Hinweise:

  • Wird während der Dateneingabe ein Trigger-Fehler beim Bestätigen bzw. Löschen eines Datensatzes zurückgegeben, wird der Fehler wie ein einmaliger indizierter Fehler behandelt. Der Fehlerdialog wird angezeigt und Sie bleiben weiter in der Dateneingabe. Auch wenn Sie eine Datenbank nur in der Designumgebung einsetzen und nicht in der Anwendungsumgebung, können Sie die Vorteile von Triggern nutzen.
  • Erzeugt ein Trigger einen Fehler, während gerade ein Befehl auf eine Datensatzauswahl ausgeführt wird, z.B. DELETE SELECTION, stoppt diese Operation sofort, d.h. die Auswahl ist u.U. nicht vollständig bearbeitet worden. Solch ein Fall erfordert vom Entwickler eine geeignete Abwicklung, wie z.B. die temporäre Erhaltung der Auswahl, Bearbeiten und Beheben des Fehlers vor Ausführen des Triggers, o. ä.

Auch wenn ein Trigger keinen Fehler zurückgibt ($0:=0), heißt das nicht zwingend, dass die Datenbankoperation erfolgreich war – es kann ein Verstoß gegen den einmaligen Index eintreten. Handelt es sich bei der Operation um die Aktualisierung eines Datensatzes, kann dieser gesperrt sein, ein E/A Fehler kann auftreten, u.v.m.. Das Prüfen erfolgt nach Ausführung des Triggers. Auf höherer Ebene des ausführenden Prozesses dagegen sind die von der Datenbank-Engine oder einem Trigger zurückgegebenen Fehler dieselben – ein Trigger-Fehler ist ein Fehler der Datenbank-Engine.

Trigger arbeiten auf der Ebene der Datenbank-Engine. Das zeigt folgendes Schema:

Trigger werden auf dem Rechner ausgeführt, auf dem die Datenbank-Engine liegt. Das ist im 4D Einzelplatzbetrieb offensichtlich. Im Mehrplatzbetrieb dagegen werden Trigger im ausführenden Prozess auf dem Server-Rechner ausgeführt (im Zwillingsprozess des Prozesses, der den Trigger ausgelöst hat), und nicht auf dem Client-Rechner .

Wird ein Trigger ausgelöst, läuft er im Kontext des Prozesses, der die Datenbankoperation versucht. Der Prozess, der die Trigger-Ausführung auslöst, heißt auslösender Prozess.
Die in diesem Kontext enthaltenen Elemente sind unterschiedlich, je nachdem ob die Datenbank mit 4D im lokalen Modus oder mit 4D Server ausgeführt wird:

  • Mit 4D im lokalen Modus arbeiten Trigger mit aktuellen Auswahlen, aktuellen Datensätzen, Tabellen im Lese-/Schreibmodus und Operationen zum Sperren des Datensatzes des auslösenden Prozesses.
  • Mit 4D Server wird nur der Kontext der Datenbank des auslösenden Client Prozesses beibehalten (gesperrte Datensätze und Stadien der Transaktion) 4D Server und nur dieser, garantiert auch, dass der aktuelle Datensatz der Trigger-Tabelle korrekt gesetzt wird. Die anderen Elemente dieses Kontexts, z.B. aktuelle Auswahlen, gehören zum Trigger Prozess.

Verwenden Sie andere Objekte der Datenbank oder der Programmiersprache der 4D Umgebung mit Bedacht, da ein Trigger auf einem anderen Rechner als dem, der den Prozess auslöst, ausgeführt werden kann. Das ist mit 4D Server der Fall!

  • Interprozessvariablen: Ein Trigger hat Zugriff auf die Interprozessvariablen des Rechners, auf dem er ausgeführt wird. Mit 4D Server kann er auch auf einen anderen Rechner, als dem mit dem auslösenden Prozess zugreifen.
  • Prozessvariablen: Jeder Trigger hat seine eigene Tabelle mit den Prozessvariablen. Ein Trigger hat keinen Zugriff auf die Prozessvariablen des auslösenden Prozesses.
  • Lokale Variablen: Sie können in einem Trigger lokale Variablen verwenden. Sie gelten während der Ausführung des Triggers; sie werden für jede Ausführung erstellt und anschließend wieder gelöscht.
  • Semaphoren: Ein Trigger kann globale Semaphoren testen und setzen, und zwar auf dem Rechner, wo er ausgeführt wird. Ein Trigger muss jedoch schnell ablaufen. Deshalb ist Vorsicht geboten, wenn Sie Semaphoren von Triggern aus testen oder setzen wollen.
  • Mengen und temporäre Auswahlen: Verwenden oder setzen Sie eine Menge bzw. eine temporäre Auswahl von Triggern heraus, arbeiten Sie auf dem Rechner, von welchem die Trigger ausgeführt werden. Im Client/Server Modus sind auf dem Client-Rechner erstellte Prozessmengen und temporäre Auswahlen (den Namen ist weder $ noch <> vorangestellt) in einem Trigger sichtbar.
  • Benutzeroberfläche: Verwenden Sie in einem Trigger KEINE Elemente der Benutzeroberfläche, wie z.B. Warnungen, Meldungen und Dialogboxen. Analog dazu sollten Sie auch den Schrittmodus für Trigger im Debugging Fenster ausgrenzen. Denn Trigger im Client/Server-Betrieb laufen auf dem Server-Rechner. Eine Meldung auf dem Server-Rechner hilft dem Benutzer auf dem Client-Rechner nicht. Deshalb sollte der aufrufende Prozess die Benutzeroberfläche verwalten.

Sie müssen Transaktionen auf der Ebene des auslösenden Prozesses verwalten und nicht auf der Trigger-Ebene. Wollen Sie während Ausführung eines Triggers mehrere Datensätze hinzufügen, ändern oder löschen, müssen Sie zuerst mit der Funktion In transaction innerhalb des Triggers prüfen, ob der auslösende Prozess derzeit in Transaktion ist. Ist das nicht der Fall, findet der Trigger höchstwahrscheinlich einen gesperrten Datensatz vor. Es macht also keinen Sinn, eine Operation auf Datensätze zu starten, wenn der auslösende Prozess nicht in Transaktion ist. Geben Sie in $0 einen Fehler zurück, der dem auslösenden Prozess signalisiert, dass die angestrebte Datenbankoperation in Transaktion ausgeführt werden muss. Sonst kann der auslösende Prozess, wenn er auf gesperrte Datensätze stößt, die Aktionen des Triggers nicht zurückfahren.

Hinweis: 4D ruft nach der Ausführung von VALIDATE TRANSACTION keine Trigger auf. Das optimiert das Zusammenspiel von Triggern und Transaktionen, denn so werden Trigger nicht zweimal aufgerufen.

Wir gehen von folgender Beispielstruktur aus:

Hinweis: Die Tabellen sind verkürzt; sie haben natürlich mehr Felder als hier gezeigt.

Wir nehmen an, dass die Datenbank das Löschen einer Rechnung zulässt. So können wir nachvollziehen, wie solch eine Operation auf Trigger-Ebene verwaltet wird (Löschen ist auf Prozessebene möglich).

Zur Wahrung der relationalen Integrität der Daten müssen beim Löschen einer Rechnung im Trigger für [Rechnungen] folgende Aktionen ablaufen:

  • Im Datensatz [Kunden] den Wert im Feld Gesamtumsatz um den Rechnungsbetrag kürzen.
  • Alle mit der Rechnung verknüpften Datensätze [Positionen] löschen.
  • Daraus ergibt sich, dass der Trigger zu [Positionen] den Wert im Feld Anzahl verkauft des Datensatzes [Artikel] verringert, der zum zu löschenden Artikel gehört.
  • Alle mit der gelöschten Rechnung verknüpften Datensätze [Zahlungen] löschen.

1. Der Trigger für [Rechnungen] muss diese Aktionen nur ausführen, wenn der auslösende Prozess in Transaktion ist, so dass bei gesperrten Datensätzen ein Zurücksetzen möglich ist.

2. Der Trigger für [Positionen] ist mit dem Trigger für [Rechnungen] verschachtelt. Der Trigger [Positionen] wird innerhalb des Triggers [Rechnungen] ausgeführt. Denn durch Aufrufen von DELETE SELECTION im Trigger [Rechnungen] erfolgt das Löschen der Positionen.

Wir gehen davon aus, dass für alle Tabellen dieses Beispiels Trigger für alle Datenbankereignisse aktiviert sind. Die Trigger sind dann folgendermaßen verschachtelt:

  • Trigger [Rechnungen] wird ausgelöst, da der auslösende Prozess eine Rechnung löscht
    • Trigger [Kunden] wird ausgelöst, da der Trigger [Rechnungen] das Feld Gesamtumsatz aktualisiert
    • Trigger [Positionen] wird ausgelöst, da der Trigger [Rechnungen] einen Artikel löscht (wiederholt)
      • Trigger [Artikel] wird ausgelöst, da der Trigger [Positionen] das Feld Anzahl verkauft aktualisiert
    • Trigger [Zahlungen] wird ausgelöst, da der Trigger [Rechnungen] eine Zahlung löscht (wiederholt)

In dieser Verschachtelung läuft der Trigger [Rechnungen] auf Ebene 1 ab, die Trigger [Kunden], [Positionen] und [Zahlungen] auf Ebene 2 und der Trigger [Artikel] auf Ebene 3.

Innerhalb der Trigger können Sie mit der Funktion Trigger level prüfen, auf welcher Ebene der Trigger abläuft. Mit dem Befehl TRIGGER PROPERTIES erhalten Sie Informationen über die anderen Ebenen.

Wird zum Beispiel ein Datensatz [Artikel] auf Prozessebene gelöscht, wird der Trigger [Artikel] auf Ebene 1, nicht auf Ebene 3 ausgeführt.

Mit Trigger level und TRIGGER PROPERTIES können Sie den Grund für eine Aktion prüfen. In unserem Beispiel wird eine Rechnung auf Prozessebene gelöscht. Löschen wir einen Datensatz [Kunden] auf Prozessebene, sollte der Trigger [Kunden] versuchen, alle mit diesem Kunden verknüpften Rechnungen zu löschen. Das heißt, dass der Trigger [Rechnungen] – wie oben beschrieben – ausgelöst wird, jedoch aus einem anderen Grund. Innerhalb des Triggers [Rechnungen] können Sie prüfen, ob er auf Ebene 1 oder 2 abgelaufen ist. Lief er auf Ebene 2 ab, können Sie prüfen, ob er aktiv wurde, weil der Datensatz [Kunden] gelöscht wurde. In diesem Fall erübrigt sich die Aktualisierung des Feldes Gesamtumsatz.

Beim Verwalten von eines Datenbankereignisses On Saving New Record Event können Sie die Funktion Sequence number aufrufen, um für die Datensätze einer Tabelle eine einmalige Kennummer zu unterhalten.

  // Trigger für Tabelle [Invoices]
 Case of
    :(Trigger event=On Saving New Record Event)
  // ...
       [Invoices]Invoice ID Number:=Sequence number([Invoices])
  // ...
 End case

 
EIGENSCHAFTEN 

Produkt: 4D
Thema: Trigger

 
SIEHE AUCH 

Methoden
Record number
Trigger event
Trigger level
TRIGGER PROPERTIES

 
ARTIKELVERWENDUNG

4D Programmiersprache ( 4D v14 R3)
4D Programmiersprache ( 4D v14 R2)
4D Programmiersprache ( 4D v13.5)
4D Programmiersprache ( 4D v14.3)
4D Programmiersprache ( 4D v14 R4)

Geerbt von : Einführung in Trigger ( 4D v11 SQL Release 6)