Diese Frage sollte eigentlich einfach zu beantworten sein, oder? Mit einem Submit werden alle Prozesse durchlaufen. Abschließend wird der Commit[1] ausgeführt. Wenn ein Fehler auftritt, wird per Rollback die Transaktion rückgängig gemacht.
Das bedeutet also das der Commit erst am Ende aller Submit-Prozesse durchgeführt wird. Normalerweise…
So einfach ist es leider nicht
Leider gibt es dabei Ausnahmen, die nicht wirklich offensichtlich sind. Die folgenden Punkte sind Fälle, bei denen bereits „mittendrin“ ein Commit durchgeführt wird. Nach dem erfolgreichen Durchlauf aller Prozesse gibt es trotzdem den abschließenden Commit.
Fall 1)
Ein „Automatic Row Process“ führt dann ein Commit aus, wenn die Option „Return Key Into Item“ genutzt wird.
Fall 2)
Ein Item-Wert wird per PL/SQL geändert, egal auf welche Weise.
:P1_ITEM := 'abc'; select 'abc' into :P1_ITEM from dual; --procedure emp_name (id IN number, emp_name OUT varchar2) emp_name (1, :P1_ITEM);
Fall 3)
Ein Item wird mit dem Aufruf von set_session_state geändert.
APEX_UTIL.set_session_state('P1_ITEM', 'abc');
Es läuft im Prinzip immer darauf hinaus, dass immer dann ein Commit durchgeführt wird, wenn ein Item-Wert in der Session geändert wird. Aber warum ist das so? Hierzu muss man wissen, wie APEX intern funktioniert.
Alle Page- und Application-Items einer User-Session (der sogenannte Session State) werden in einer APEX-internen Tabelle persistiert. Wenn man ein Item ändert, wird also auf jeden Fall in diese interne Tabelle geschrieben. Nach dem Ändern des Session State führt APEX ein Commit durch — einen sogenannte Implicit Commit.
Das bedeutet übrigens auch, dass ein Implicit Commits selbst per Seiten-Aufruf erzeugt werden kann (per Link bzw. „Redirect to Page in this Application“, nicht Submit). Nämlich dann, wenn ein Item im Pre-Rendering-Bereich in der Session geändert wird.
Ausnahme von der Ausnahme
Um es noch ein wenig komplizierter zu machen, gibt es noch folgende Ausnahme: Wenn das Item geändert wird, muss sich der Wert tatsächlich unterscheiden. Beim Setzen von identischen Werten, wird kein Implicit Commit ausgeführt. APEX prüft also bei Ausführung von set_session_state auf Differenzen zwischen altem und neuem Wert.
Die Implicit Commits kann man übrigens ebenso im Debug sehen, wie den abschließenden „Final commit“. Den Debug-Parameter muss man dafür nur auf „LEVEL9“ stellen.
http://apexurl/ords/f?p=100:1:11111111::LEVEL9
Betrifft mich dieses Problem?
Im Allgemeinen ist das nicht weiter schlimm und in 99% der Fälle hat das keine Auswirkungen. Es gibt aber Situationen, da sollte man sich dieses Verhaltens bewusst sein.
Denken wir an ein Formular, in dem ein neuer User mit seinen Rollen angelegt werden soll. Mit Klick auf „Speichern“ werden zwei Prozesse ausgelöst: Ein „Automatic Row Process“ um den User zu speichern und ein PL/SQL-Prozess, mit dem die Rollen zum User in einer weiteren Tabelle gespeichert werden. Damit wir die Rollen zuordnen können, setzen wir die P1_USER_ID per Option „Return Key Into Item“. (Hier passiert der Implicit Commit).
Wenn nun beim Ausführen des zweiten Prozesses etwas schief geht, wird der Benutzer ohne die ausgewählten Rollen persistiert. Und dass ein Datensatz ohne weitere zugehörige Daten gespeichert wird, kann in manchen Anwendungsfällen ein Problem sein.
Wie komme ich da raus?
Am besten wäre es natürlich, wenn das Speichern der APEX-Session unabhängig von unseren User-Daten passieren würde. Aber das Problem ist schon länger bekannt und ich denke nicht, dass das kurzfristig von Oracle geändert wird.
Wir als Entwickler können aber in diesen komplexeren Fällen darauf achten, dass die Item-Änderungen die einen Implicit Commit verursachen am Anfang der Prozesskette durchgeführt werden. Die darauf folgenden Prozesse die in einer Transaktionsklammer zusammengehalten werden sollen, sollten dann ohne Session State Änderungen auskommen.
In unserem Beispiel kann man sich z.B. den Primary Key separat aus der Sequence holen, bevor man den „Automatic Row Process“ startet. Auf „Return Key Into Item“ kann man dann verzichten und es findet kein Implicit Commit statt.
Quellen:
- https://community.oracle.com/thread/710781 (Problem mit Return Key Into Item)
- https://jeffkemponoracle.com/2014/02/20/apex_util-set_session_state-may-or-may-not-commit/ (Beschreibung mit Test Cases)
- http://www.danielmcghan.us/2012/08/implicit-commits-in-apex.html (ausführliche Erklärung)
[1] Als Commit bezeichnet man bei Datenbanken den erfolgreichen Abschluss einer Transaktion. Das Ergebnis der Verarbeitungsschritte wird damit dauerhaft gespeichert.
Eine Antwort auf „Wann findet in APEX ein Commit statt?“
Gut erklärt, Danke!