Oracle Forms Was validiere ich wo? Autor: Torsten Pattberg, Opitz Consulting Gummersbach GmbH DOAGNews Q1_2004 Dieses Werk ist urheberrechtlich geschützt. Die dadurch begründeten Rechte, insbesondere die der Übersetzung, des Nachdrucks, des Vortrags, der Entnahme von Abbildungen und Tabellen, der Funksendung, der Mikroverfilmung oder der Vervielfältigung auf anderen Wegen und der Speicherung in Datenverarbeitungsanlagen, bleiben, bei auch nur auszugsweiser Verwertung, vorbehalten. Eine Vervielfältigung dieses Werkes oder von Teilen dieses Werkes ist auch im Einzelfall nur in den Grenzen der gesetzlichen Bestimmungen des Urheberrechtes der Bundesrepublik Deutschland vom 9. September 1965 in der jeweils geltenden Fassung zulässig. Sie ist grundsätzlich vergütungspflichtig. Zuwiderhandlungen unterliegen den Strafbestimmungen des Urheberrechtsgesetzes.
Oracle Forms bietet zwei Arten der Daten-Validierung, zum einen auf Feld-Ebene mittels der WHEN-VALIDATE-ITEM Trigger, zum anderen auf Datensatz-Ebene durch den WHEN- VALIDATE-RECORD Trigger. Die erste Methode wird grundsätzlich für einfache Prüfungen verwendet. Das entsprechende Feld kann dabei erst dann verlassen werden, wenn sein Inhalt gültig, etwa eine eingegebene Email-Adresse syntaktisch korrekt ist. Die zweite Methode sollte in der Regel immer dann Einsatz finden, wenn Abhängigkeiten zwischen Feldern geprüft werden, ob also zum Beispiel ein eingegebener Wert größer als eine zweite Eingabe ist. Ein Datensatz kann also ungültig sein, obwohl die einzelnen Felder für sich gültig sind. Information kontra Navigation Diese Prüfung auf Datensatz-Ebene hat den Nachteil, dass eventuelle Fehleingaben erst spät, das heißt konkret beim Speichern oder beim Wechsel des entsprechenden Datensatzes angezeigt werden. Handelt es sich um eine komplexe Maske so passiert es durchaus, dass eine Reihe von Fehlermeldungen einzeln nacheinander abgearbeitet werden müssen, bevor der gültige Datensatz gespeichert werden kann. Möchte man Abhängigkeiten auf Feld-Ebene prüfen, so steht man vor dem Problem, dass bei einer erkannten Fehleingabe die Navigation aus dem aktuellen Feld nicht mehr möglich ist. Was aber, wenn der eigentliche Fehler nicht im gerade geprüften Feld liegt, sondern in einem von ihm abhängigen? Der Anwender muss dann zunächst im aktuellen Feld eine Eingabe machen, die in diesem Moment gültig ist. Danach kann er in das abhängige Feld navigieren, um dort die Eingabe zu korrigieren. Anschließend gibt er im Ausgangsfeld wieder den richtigen Wert ein. Ein Beispiel aus der Praxis Bei der Erfassung eines Vertrags muss der Anwender angeben, wann dieser beginnt und wann er endet. Eine sinnvolle Prüfung auf die Felder Beginn-Datum und Ende-Datum ist, dass das Ende nicht vor dem Beginn liegen kann. Der Vertrag läuft vom 1. Januar 2003 bis zum 31. Dezember 2003, der Anwender gibt aber beim Beginn-Datum versehentlich den 1. Januar 2004 ein. Im Normalfall würde er dann bei der Eingabe des Ende-Datums auf den Fehler aufmerksam gemacht werden, muss dieses dann aber zunächst z.b. auf den 31. Dezember 2004 ändern, um aus dem Feld heraus navigieren zu können. Dann gibt er das richtige Beginn-Datum ein und korrigiert anschließend das Ende-Datum.
Geht s nicht einfacher? Wir wollen Fehleingaben in voneinander abhängigen Feldern auf Feld-Ebene erkennen und anzeigen, die Navigation aber weiterhin ermöglichen. Natürlich darf ein solcher ungültiger Datensatz trotzdem nicht gespeichert werden können. Wir müssen die Prüfung also so schreiben, dass auf Feld-Ebene zwar der Fehler angezeigt wird, aber keine FORM-TRIGGER- FAILURE Exception geworfen wird. Diese würde ja gerade die Navigation verhindern. Auf Datensatz-Ebene sorgen wir aber nach der Anzeige des Fehlers weiterhin durch Werfen einer FORM-TRIGGER-FAILURE Exception dafür, dass der ungültige Satz nicht gespeichert werden kann. Wir wollen jedoch den gleichen Programmcode für beide Ebenen benutzen, damit wir nicht für jede Prüfung zwei nahezu identische Prozeduren anlegen müssen. Das würde die Form insgesamt unübersichtlich und schwer wartbar machen. Der Quellcode sollte also in etwa so aussehen: PROCEDURE Verbiete_Beginn_nach_Ende IS IF :BLOCK.N_DATUM > :BLOCK.ENDE_DATUM THEN Validierung.Check_if_FORM_TRIGGER_FAILURE; END Verbiete_Beginn_nach_Ende; Wir wollen die genaue Funktionsweise an dem obigen Beispiel verdeutlichen. Dazu legen wir ein Forms-Modul an, dessen Struktur wie folgt aussieht:
Die Prozedur Verbiete_Beginn_nach_Ende wird hierbei sowohl im WHEN-VALI- DATE-RECORD Trigger als auch in den WHEN-VALIDATE-ITEM Triggern der beiden Felder aufgerufen. Die weitere Logik liegt in dem Package Validierung, das wie folgt aussieht: PACKAGE Validierung IS PROCEDURE Check_if_FORM_TRIGGER_FAILURE; PROCEDURE WHEN_NEW_ITEM; Last_Trigger_Item VARCHAR2(61); END Validierung; PACKAGE BODY Validierung IS PROCEDURE Check_if_FORM_TRIGGER_FAILURE IS V_Trigger_Item VARCHAR2(61); V_Alert NUMBER(9); V_Trigger_Item := NAME_IN ('System.Trigger_Item'); IF V_Trigger_Item IS NOT NULL OR Last_Trigger_Item IS NULL OR NAME_IN ('System.Cursor_Item')!= Last_Trigger_Item THEN V_Alert := SHOW_ALERT('Stop_Alert'); IF V_Trigger_Item IS NULL THEN RAISE FORM_TRIGGER_FAILURE; Last_Trigger_Item := V_Trigger_Item; END Check_if_FORM_TRIGGER_FAILURE; PROCEDURE WHEN_NEW_ITEM IS Last_Trigger_Item := NULL; END WHEN_NEW_ITEM; END Validierung; Die Prozedur WHEN_NEW_ITEM wird im WHEN-NEW-ITEM-INSTANCE Trigger des Blocks aufgerufen. Funktionsweise Die FORM-TRIGGER-FAILURE Exception wird nur auf Datensatz-Ebene geworfen, da das System.Trigger_Item nicht im WHEN-VALIDATE-RECORD Trigger, sondern nur im WHEN-VALIDATE-ITEM Trigger gefüllt ist. Genau aus diesem Grund wird die Fehlermeldung auf Feld-Ebene auch immer angezeigt Auf Datensatz-Ebene geschieht dies nur dann, wenn sie nicht schon vorher im WHEN-VALIDATE-ITEM Trigger des aktuellen Feldes ausgegeben wurde (Last_Trigger_Item is NULL) oder wir uns mittlerweile in einem anderen Feld befinden (NAME_IN ('System.Cursor_Item')!= Last_Trigger_Item). Der WHEN-NEW-ITEM Trigger ist nur für einen Fall zuständig:
Befindet man sich in einem ungültigen Feld, navigiert heraus (hierbei wird die Fehlermeldung angegeben), navigiert dann wieder zurück in das Feld und versucht dann zu Speichern, so würde ohne das Löschen des Last_Trigger_Item keine Fehlermeldung angezeigt. Was ist zu beachten? Beim Anlegen der Prüfungen ist zu beachten, ob die zu prüfenden Felder required sind oder nicht. Insbesondere muss hierauf die Bedingung, wann der Satz ungültig sein soll, abgestimmt werden. Zu Überlegen ist, was bei leeren und nicht-required Feldern geschehen soll. In unserem Beispiel wird die Fehlermeldung nur dann angezeigt, wenn beide Felder gefüllt sind, denn anderenfalls wäre beim Vergleich mit NULL die IF-Bedingung nicht erfüllt. Da beide Felder obligatorisch sind, müssen die beiden Daten auf jeden Fall eingegeben werden, die Prüfung kann dann korrekt ablaufen Torsten Pattberg Opitz Consulting Gummersbach GmbH torsten.pattberg@opitz-consulting.de Tel.: +49 2261 6001-0 Tel.: +49 2261 6001-61