21. August 2007

Forms Beta-Test, Tag 2

Der zweite Tag des Beta-Tests startete mit einem Überblick des neuen Oracle Application Servers. Vier Sessions wurden diesbezüglich heute präsentiert. Die restliche Zeit blieb zum Betatesten von Oracle Forms 11g.

Am Ende des Tages lud Oracle uns in ein nettes Restaurant nahe des Headquarters ein.

20. August 2007

Einladung zum Oracle Forms Beta-Test

Oracle HQ in Redwood Shores lud mich zum diesjährigen Beta-Test der Fusion Middleware ein.

Heute starteten wir im Headquarter zusammen mit 16 anderen eingeladenen Firmen. Zusätzlich zu uns Testern unterstützte Oracle diese Veranstaltung noch mit nahezu 50 Oracle Cracks. Zum Beispiel den Oracle Forms Chefentwickler Duncan Mills.

Oracle entwickelte umfangreiche Test-Szenarien für die kommenden 5 Tage. Somit werden wir in der Lage sein, tief in all die neuen Produkte reinzuschauen.

Die Stimmung in der Mannschaft ist grossartig!

3. August 2007

Setze Record-Status auf Query nach dem POST-QUERY

In Blöcken mit einem POST-QUERY-trigger hat man oft das Problem, dass der Record-Status auf CHANGED wechselt, wenn Non-Basetable-Items verändert wurden im POST-QUERY.

Wenn man nun den Record-Status zurück auf QUERY setzt, ist das auf jeden Fall schon mal eine gute Vorgehensweise. Dazu erstellen wir eine Prozedur zum Setzen des Record-Status auf QUERY:

PROCEDURE Set_Record_Query_Status IS
BEGIN
Set_Record_Property (NAME_IN ('SYSTEM.TRIGGER_RECORD'),
NAME_IN ('SYSTEM.TRIGGER_BLOCK'),
STATUS,
QUERY_STATUS);
END;

dieser wird dann im POST-QUERY-Trigger aufgerufen:

BEGIN
SELECT someColumns
INTO :myBlock.nonBasetable_Item
FROM myTable
WHERE someFilter;

Set_Record_Query_Status;
END;

Nachdem nun jeder Datensatz auf QUERY-Status gesetzt wurde hat man mit den Daten des Blockes auch kein Problem mehr.

Wichtig: Wenn der POST-QUERY Basetable-Items im Datensatz verändert muss man die erzeugten Datensatz-Sperren zurücksetzen, falls der Block-Locking-Modus auf Immediate gesetzt ist. Dieses Problem kann folgendermassen gefixt werden:


BEGIN
Set_Block_Property ('myBlock', LOCKING_MODE, Delayed);

SELECT someColumns
INTO :myBlock.nonBasetable_Item
FROM myTable
WHERE someFilter;

Set_Block_Property ('myBlock', LOCKING_MODE, Immediate);

Set_Record_Query_Status;
END;



try it
Gerd

1. August 2007

Oracle Certified Trainer

Seit Freitag bin ich nun auch Oracle Certified Trainer. Dies ist die Voraussetzung dafür, in der Oracle University arbeiten zu dürfen.

20. Juli 2007

Neustart meines Forms Framework-Projektes

Open-Source-Projekte sollte man dort hosten, wo sie

- einfach zu benutzen
- einfach zu administrieren
- einfach zu finden sind

und vor zwei Tagen fand ich Google Code. Dies ist eine phantastische Plattform, um Projekte zu sharen!

http://code.google.com/p/forms-framework/

Zur Zeit deploye ich gerade die Haupt-PL/SQL Library und das Forms Template

Viel Spass mit dem Projekt und seinen nächsten Releases
Gerd

19. Juli 2007

Check Form_Success für jedes Built-In

Alle Built-Ins haben das Problem, das sie keine echten Exceptions werfen. Nur im ON-ERROR

Beispiel: Sie möchten zum Block Customer navigieren. Dabei unterlief ihnen ein Schreibfehler:

Go_Block ('CUSTOMR');
Do_something_after_Go_Block;

Go_Block kann nicht zu dem angegebenen Block navigieren, da CUSTOMR nicht existiert. Es wird jedoch keine Exception für innerhalb des PL/SQL-Blockes erzeugt. Das heisst, der Code wird nicht unterbrochen und die Abarbeitung von Do_something_after_Go_Block startet. Das ist immer ein grosses Problem!

Lösung: Erzeuge eine Prozedur Check_Builtin

PROCEDURE Check_Builtin IS
BEGIN
IF NOT Form_Success THEN
RAISE Form_Trigger_Failure;
END IF;
END;

Benutze diese Prozedur nach jedem Built-In:

BEGIN
Go_Block ('CUSTOMR');
Check_Builtin;
Do_something_after_Go_Block;
EXCEPTION
WHEN FORM_TRIGGER_FAILURE THEN
-- do something ...
END;

Desweiteren kann man ein eigenes Built-In schreiben für Goto_Block anstelle von Go_Block: Diese neue Prozedur arbeitet intern mit dem neuen Check_Builtin

PROCEDURE Goto_Block (P_Block IN VARCHAR2) IS
BEGIN
Go_Block (P_Block);
Check_Builtin;
Do_something_after_Go_Block;
END;

als nächstes:

BEGIN
Goto_Block ('CUSTOMR');
Do_something_after_Go_Block;
EXCEPTION
WHEN FORM_TRIGGER_FAILURE THEN
-- do something ...
END;

Wichtig: Wenn sie diese Technik anwenden müssen sie ein Exception-Handling schreiben, dass den FORM_TRIGGER_FAILURE abfängt und behandelt.

Diese Technik ist identisch zu Oracle's Check_Package_Failure-Routine, diese kann jedoch nur genutzt werden, wenn in der Maske mindestens eine Master-Detail-Relation existiert.

benutze Check_Builtin
Gerd

10. Juli 2007

Zusammenführung grosser Default-Where-Bedingungen

Der einfachste Weg um Where-Bedingungen zu konkatinieren ist:

Beispiel: Eine leere Maske mit der Tabelle DEPT. In diesem Beispiel soll es drei Regeln geben, nach denen die Default-Where-Bedingung aufgebaut wird. Das Problem ist: Ab dem zweiten IF muss man jedesmal überprüfen, ob in V_Default_Where schon etwas drinsteht. Falls ja wird ein ' AND ' dran konkatiniert vor jedem neuen Teilstring.

DECLARE
V_Default_Where VARCHAR2 (2000);
BEGIN
IF Rule_1_is_TRUE THEN
V_Default_Where := 'DEPTNO IN (10, 20, 30)';
END IF;

IF Rule_2_is_TRUE THEN
IF V_Default_Where IS NOT NULL THEN
V_Default_Where := V_Default_Where ||
' AND DNAME != ''SALES'' ';
ELSE
V_Default_Where := 'DNAME != ''SALES'' ';
END IF;
END IF;

IF Rule_3_is_TRUE THEN
IF V_Default_Where IS NOT NULL THEN
V_Default_Where := V_Default_Where ||
' AND LOC IS NOT NULL';
ELSE
V_Default_Where := 'LOC IS NOT NULL';
END IF;
END IF;

Set_Block_Property ('DEPT', DEFAULT_WHERE,
V_Default_Where);
Go_Block ('DEPT');
Execute_Query;
END;

Diese IF's sind nicht wartbar. Änderungen in der Where-Bedingung bedeuten immer zwei Änderungen im Code:

...
V_Default_Where := V_Default_Where ||
' AND LOC IS NULL';
ELSE
V_Default_Where := 'LOC IS NULL';
...

Ein besserer Ansatz ist die Benutzung von ' AND ' bei jeder Konkatinierung:

DECLARE
V_Default_Where VARCHAR2 (2000);
BEGIN
IF Rule_1_is_TRUE THEN
V_Default_Where := ' AND DEPTNO IN (10, 20, 30)';
END IF;

IF Rule_2_is_TRUE THEN
V_Default_Where := V_Default_Where ||
' AND DNAME != ''SALES'' ';
END IF;

IF Rule_3_is_TRUE THEN
V_Default_Where := V_Default_Where ||
' AND LOC IS NOT NULL';
END IF;

Set_Block_Property ('DEPT', DEFAULT_WHERE,
Substr (V_Default_Where, 6));
Go_Block ('DEPT');
Execute_Query;
END;


der Substr (V_Default_Where, 6) eliminiert zum Schluss das führende ' AND '.

Sehr einfacher und wartbarer Code!

PS: In den englischen Kommentaren gibt es eine Erklärung dafür, dass ich nicht den Trick mit "1=1" benutze

27. Juni 2007

Deutscher Wochentag

Das grosse Problem von "to_char (sysdate, 'D')" ist, dass abhängig vom NLS unterschiedliche Resultate herauskommen:

Sonntag ist der erste Wochentag in den USA
Montag ist der erste Wochentag in Deutschland

Grün-Donnerstag 2000 zum Beispiel:

Green_Thursday := to_date ('23.03.2000', 'DD.MM.YYYY');
in den USA: to_char (Green_Thursday, 'D') = 5
in der BRD: to_char (Green_Thursday, 'D') = 4

Das ist sub-optimal, da die Formatmaske abhängig von der NLS ist.

Meine Lösung in diesem Fall ist: Die Funktion German_Weekday

FUNCTION German_Weekday (P_Date IN DATE)
RETURN NUMBER IS
V_Delta NUMBER;
BEGIN
-- Referenz-Tag: Grün Donnerstag 2000 = Tag 4 in Deutschland
V_Delta := TO_NUMBER (TO_CHAR (TO_DATE ('23.03.2000',
'DD.MM.YYYY'),
'D')) - 4;
RETURN (TO_NUMBER (TO_CHAR (P_Date-V_Delta, 'D')));
END;

und American_Weekday

FUNCTION American_Weekday (P_Date IN DATE)
RETURN NUMBER IS
V_Delta NUMBER;
BEGIN
-- Referenz-Tag: Grün Donnerstag 2000 = Tag 5 in USA
V_Delta := TO_NUMBER (TO_CHAR (TO_DATE ('23.03.2000',
'DD.MM.YYYY'),
'D')) - 5;
RETURN (TO_NUMBER (TO_CHAR (P_Date-V_Delta, 'D')));
END;

diese Funktion gibt nun immer den korrekten deutschen Wochentag zurück, unabhängig von der NLS.

viel Spass damit
Gerd

23. Juni 2007

Forms 11g Erscheinungsdatum

Keiner weiss, wann Forms 11g herauskommen wird.

Das neueste offizielle Statement zu diesem Thema ist:

"Version 11 von Forms wird in der Version des Application Server Version 11 enthalten sein, das im Fiscal Jahr 2008 erscheinen wird."

Nachzulesen im OTN-Diskussions-Forum

22. Juni 2007

Oracle Develop 2007 in München

Anfang der Woche besuchte ich die Oracle Develop im Arabella Sheraton München. Sehr schönes Hotel, jedoch mussten wir zwischen den Vorträgen immer zwischen unterschiedlichen Gebäuden hin und herlaufen. Und die Kaffeemaschine war natürlich in dem Gebäude in dem nur 1 der 5 parallelen Slots stattfand

Tag 1:

Montag früh startete ich mit Frank Nimphius' Vortrag über Forms, die Zukunft von Forms und die Integration in Java, SOA, ...

Marc Sewtz und sein "SQL Developer Features" war nett, da wir eine Fülle neuer Feature des aktuellen Releases zu sehen bekamen

Nach dem Mittagessen gab es einen phantastischen Bryn Llewellyn mit "PL/SQL Performance: Debunking the Myths". Einfach klasse! Das nächste Mal kriegt er hoffentlich zwei Stunden und nicht nur eine.

Parallel zu den Vorträgen gab es 3 Labs von Oracle. Montag nachmittag schaute ich mir die neue APEX 3.0 Version an.


Tag 2:


Schlechte Nachrichten an der Kaffee-Front. Wir mussten weiterhin ein paar 100m im Freien mit unserem Kaffee von Hotel A nach Hotel B laufen, wenn wir dort was trinken wollten.

Dienstags schaute ich mir das nächste Lab an: "Developing and Deploying Oracle and PHP". Sehr interessant zu sehen, wie einfach PHP in Oracle integriert werden kann.

Danach präsentierte Bryn "PL/SQL-Enhancements of the new Oracle DB 11g". Ich liebe die Compound Triggers! Zusammen mit all den anderen neuen Techniken. Sequencen, die nun direkt angesprochen werden können und nicht mehr über DUAL !!

Nach dem Mittagessen präsentierte Frank Nimphius "Building Rich UI using JavaServer Faces and AJAX". Dies war ein weiteres Highlight Konferenz.

Interessant, wieviel Microsoft-Themen in den Vorträgen adressiert wurden. "Microsoft Interoperability with Oracle Fusion Middleware" zeigte die SOA-Integration, die mit dem Office-Toolstack ermöglicht wird. Schön zu sehen, wie andere Firmen das machen.


Zusammenfassung:


Vorträge und Labs: TOP
Kein Kaffee: FLOP

10. Mai 2007

Neues Forms Look & Feel

Gute Nachrichten für alle Forms-Entwickler, die ihre Masken einmal in einem neuen Look and Feel sehen möchten.

In Grant's neuestem Interview sprach er mit Francois Degrelle, über sein Forms Look & Feel White Paper von April 2007.

17. April 2007

Schnellerer Forms-Start mit Synchronize

I konnte es gar nicht glauben, aber es klappt in manchen Fällen!

Wenn man in einer Forms-Anwendung den Eindruck hat, dass der Forms-Start zu langsam vonstatten geht, dann sollte man einen Synchronize einbauen um den Bildschirm beim Starten zu refreshen. Der Anwender denkt nun, dass die Maske schneller startet, intern wurde jedoch nur das Refreshen des Bildschirms vorgezogen.

WHEN-NEW-FORM-INSTANCE - trigger

BEGIN
synchronize;
-- your WHEN-NEW-FORM-INSTANCE-code
END;

probier es aus!

5. April 2007

EOUC 2007 wurde abgesagt

hmmmmmm...

die EMEA-Oracle-User-Council-Konferenz in Amsterdam wurde gerade abgesagt:

EOUC 2007

Update Dez. 2007: mittlerweile wurde sogar diese URL gecancelt

4. April 2007

Multi-Select from DUAL

Ein einfacher Weg um Datensätze aus dem Nichts zu erzeugen ist ein einfacher SELECT mit einem CONNECT BY gegen DUAL.

Wenn man zum Beispiel in einer Forms-LOV die letzten 12 Monate anzeigen möchte, dann benötigt man eine Record-Group, die genau 12 Datensätze zurückgibt. Danach kombiniert man das noch mit dem Sysdate:

SELECT Level LVL
FROM Dual
CONNECT BY Level <= 12;

jetzt bringt man Sysdate ins Statement:

SELECT add_months (trunc (sysdate, 'MM'), -1*Level) Month
FROM Dual
CONNECT BY Level <= 12;

MONTH
--------
01.03.07
01.02.07
01.01.07
01.12.06
01.11.06
01.10.06
01.09.06
01.08.06
01.07.06
01.06.06
01.05.06

ist das nicht eine genial einfache Lösung für die Selektion der letzten 12 Monate?

14. März 2007

Assertions

Assertions im Sourcecode sind Alltag in Java und anderen Programmiersprachen. Warum also nicht auch bei uns in PL/SQL?

Das ist eine gute Frage und ich löste sie für meine Fragestellungen mit dieser Technik:
DECLARE
e_Assertion EXCEPTION;
BEGIN
IF condition1 = 'value'
OR boolean = TRUE
OR something_else THEN
RAISE e_Assertion;
END IF;

-- weiterer Code:
...
EXCEPTION
WHEN e_Assertion THEN
NULL;
WHEN OTHERS THEN
-- when-others-exception-handling
END;

In diesem Beispiel schreibt man alle negativen Assertions untereinander und startet entsprechende Exceptions, die im Exception-Handling nichts tun.

z.B.
PROCEDURE Double_Manager_Salary (P_EMPNO IN NUMBER, P_JOB IN VARCHAR2) IS
e_Assertion EXCEPTION;
BEGIN
IF P_Job != 'MGR' THEN
RAISE e_Assertion;
END IF;

UPDATE EMP SET
SAL = SAL * 2
WHERE EMPNO = P_EMPNO;

EXCEPTION
WHEN e_Assertion THEN
NULL;
END;

was wir hier sehen ist sehr einfach: Wenn man annimmt, dass nur Manager das doppelte Gehalt bekommen, dann kann man die Prozedur direkt nach dem Start abbrechen. Man springt in die e_Assertion-Exception und tut nichts.

Try it
Gerd