20. Dezember 2006

Undo

Daten aus der Datenbank abfragen und ändern ist in Forms relativ einfach. Aber was ist, wenn der Anwender einen einfachen Undo auf seine Änderungen machen will?

Einfach eine neue Abfrage starten ist der einfachste Weg. Beachtet werden muss:

- in einem Multi-Record-Block muss man sich nach der Abfrage wieder auf dem aktuellen Datensatz positionieren
- wenn die Abfrage durch eine ENTER-QUERY-Selektion geschah kann man nicht zu dem Datensatz springen, da die Abfragemenge unterschiedlich gross ist.

Man muss also eine andere Technik verwenden.

Die Lösung ist diese Funktion. Alle Datenbank-Felder bekommen ihren alten Wert zurück:


PROCEDURE Undo IS
V_Block VARCHAR2 (30) := :SYSTEM.CURSOR_BLOCK;
V_Field VARCHAR2 (61);
V_Item VARCHAR2 (61);
BEGIN
Validate (Item_Scope);
IF :SYSTEM.RECORD_STATUS = 'CHANGED' THEN
V_Field := Get_Block_Property (V_Block, FIRST_ITEM);
V_Item := V_Block || '.' || V_Field;
WHILE V_Field IS NOT NULL
LOOP
IF Get_Item_Property (V_Item, ITEM_TYPE)
IN ('DISPLAY ITEM', 'CHECKBOX', 'LIST',
'RADIO GROUP', 'TEXT ITEM')
AND Get_Item_Property (V_Item, BASE_TABLE) = 'TRUE'
THEN
COPY (Get_Item_Property (V_Item, DATABASE_VALUE),
V_Item);
END IF;
V_Field := Get_Item_Property (V_Item, NextItem);
V_Item := V_Block || '.' || V_Field;
END LOOP;
END IF;
END;


Best practice wäre, wenn man diesen Undo vom Menü aus starten könnte (z.B. Bearbeiten - UNDO) oder durch einen Hotkey.

15. Dezember 2006

Gleich und UnGleich

Manchmal muss man zwei Variablen auf Gleichheit prüfen.

Wenn man einfach nur "IF A = B THEN" schreibt, dann ist das keine Lösung. Falls eine der Variablen NULL ist, dann wird das Ergebnis des Ausdrucks ebenfalls NULL und das IF-Statement interpretiert dies als FALSE. Man benötigt also eine andere Vorgehensweise:

FUNCTION Equal (P_String1 IN VARCHAR2,
P_String2 IN VARCHAR2) RETURN BOOLEAN IS
BEGIN
IF P_String1 = P_String2
OR (P_String1 IS NULL AND P_String2 IS NULL) THEN
RETURN (TRUE);
ELSE
RETURN (FALSE);
END IF;
END;

FUNCTION UnEqual (P_String1 IN VARCHAR2,
P_String2 IN VARCHAR2) RETURN BOOLEAN IS
BEGIN
IF P_String1 != P_String2
OR ( P_String1 IS NULL
AND P_String2 IS NOT NULL)
OR ( P_String1 IS NOT NULL
AND P_String2 IS NULL) THEN
RETURN (TRUE);
ELSE
RETURN (FALSE);
END IF;
END;

Nun kann man Equal und UnEqual sehr einfach benutzen:

IF UnEqual (Var1, Var2) THEN
-- do something
ELSE
-- do something different
END IF;


versuch's

5. Dezember 2006

ON-ERROR und ON-MESSAGE-Trigger

Viele Entwickler haben das Problem mit Meldungen, die in der Maske angezeigt werden, zum Beispiel "FRM-40401: No changes to save".

Sie suchen dann nach Workarounds und manipulieren dann oft den :system.message_level:

KEY-COMMIT - Trigger auf Form-Ebene (Quick + Dirty)
BEGIN
:System.Message_Level := 25;
COMMIT;
:System.Message_Level := 5;
END;

oder speichern ihn zwischen:

DECLARE
V_Message_Level NUMBER;
BEGIN
V_Message_Level := :System.Message_Level;
:System.Message_Level := 25;
COMMIT;
:System.Message_Level := V_Message_Level;
END;


Das sind keine Best Practices. Diesen Code müsste man in hunderten Prozeduren unterbringen.

Fehler und Meldungen filtern ist die Lösung für ein gutes Message-Handling. Diese Prozedur zeigt die Technik:

ON-ERROR - Trigger auf Form-Ebene
DECLARE
V_Error_Code       NUMBER;
V_Error_Text       VARCHAR2 (2000);
V_DBMS_Error_Code  NUMBER;
V_DBMS_Error_Text  VARCHAR2 (2000);
BEGIN
V_Error_Code      := Error_Code;
V_Error_Text      := Error_Text;
V_DBMS_Error_Code := DBMS_Error_Code;
V_DBMS_Error_Text := DBMS_Error_Text;

IF V_Error_Code IN (40401, 40405) THEN
/*
|| 40401, 40405 - no changes to save / apply  get filtered
*/
NULL;
ELSIF V_Error_Code IN (-1034, -3114) THEN
/*
|| -1034, -3114 - not connected to database
*/
Message ('Not connect to database, exiting Form');
Exit_Form (no_validate);
ELSIF V_Error_Code IN (40508, 40735)
AND   V_DBMS_Error_Code BETWEEN -20999 AND -20000 THEN
/*
|| -20000 errors are raised by RAISE_APPLICATION_ERROR 
|| They are handled in a different way
*/
Show_and_Log_DB_Error (V_DBMS_Error_Text);
ELSE
/*
|| All other errors went into Show_and_Log_Error, where they
|| get inspected, analyzed and logged.
*/
Show_and_Log_Error (V_Error_Code);
END IF;
END;