Autocommit und Before Commit Events – Keine Liebesgeschichte
Ich bin gerade über etwas gestolpert was mir das Leben etwas schwer gemacht hat. Vielleicht hilft dieser Artikel anderen nicht in das gleiche Problem zu laufen. Aber fangen wir vorne an.
Ein nettes Before Commit Pattern
Ein Szenario was ich schon sehr oft gesehen habe ist folgendes:
Wenn sich ein bestimmtes Attribut eines Datensatzes verändert soll eine Aktion ausgelöst werden. Ein Beispiel hierfür wäre ein Objekt mit einem Status, welches bei jeder Statusänderung eine Benachrichtigung erzeugen soll.
Ein ziemlich praktisches Pattern hierfür ist es, in einem Before Commit Event, das zu committende Objekt in seinem alten Zustand aus der Datenbank zu retrieven und mit dem aktuellen zu vergleichen. Sollten Änderungen festgestellt werden wird die Benachrichtigung erzeugt.
Das ganze würde folgendermaßen aussehen:
Das ganze funktioniert hervorragend, sofern dieses Before Commit Event läuft bevor das Objekt in der Datenbank geupdatet ist. Und hier kann uns nun leider das Autocommit in die Quere kommen.
Das Mendix Autocommit „Feature“
Mendix ist bestrebt dem LowCode Entwickler dabei zu helfen seine Daten möglichst konsistent zu halten. Hierzu gibt es unter Anderem ein Feature namens Autocommit.
Wird auf einem Objekt eine Assoziation zu einem anderen Objekt gesetzt und dieses andere Objekt existiert zu diesem Zeitpunkt noch nicht in der Datenbank, so wird dieses nicht existierende Objekt automatisch auch committed (autocommitted). Somit muss sich der Entwickler nicht sorgen eine Assoziation gesetzt zu haben die so später gar nicht in der Datenbank existiert.
Wird in diesem Beispiel ein Objekt vom Typ OtherEntity committed welches auf ein nicht committetes Objekt vom Typ Entity verweist, so wird letzteres autocommitted.
Das Problem
Hierbei entsteht möglicherweise ein Problem. Mendix triggert bei einem Autocommit keine Events. Es schreibt die Daten einfach in die Datenbank. Dies ist insofern sinnvoll, als dass der Entwickler ja kein Commit angestoßen hat. Es birgt jedoch gefahren wenn z.B. ein Before Commit Event darauf angewiesen ist VOR dem Commit zu laufen (Wie das oben beschriebene Pattern). Wird nämlich das zuvor autocommittete Objekt nochmals explizit committed, so befindet es sich bereits in der Datenbank. Der Vergleich zwischen dem alten und dem neuen Status führt also zu keinem brauchbaren Ergebnis mehr.
Vorsicht bei der Commit Reihenfolge
In diesem Beispiel findet durch das Commit von OtherEntity ein Autocommit von Entity statt. Das Before Commit Event auf Entity wird erst getriggert, wenn Entity im Microflow committet wird. Zu diesem zeitpunkt stehen die Änderungen schon in der Datenbank. Das notification Pattern wird in jedem Fall fehlschlagen.
In diesem Fall wird zuerst der Commit auf Entity ausgeführt. Das notification Pattern funktioniert wie erwartet. Es findet kein Autocommit statt.
Fazit
Ich nehme zwei Erkenntnisse für mich mit.
- Die Reihenfolge in der Objekte committed werden ist wichtig. Unterschiedliche Reihenfolgen können zu unterschiedlichen Ergebnissen führen. Ideal ist es hier wenn man versucht durch die Reihenfolge ein Autocommit zu verhindern.
- Da erstens nicht immer möglich ist (Es könnten z.B. 3 oder Mehr Objekte so aufeinander verweisen, dass ein Autocommit nicht verhindert werden kann), sollte man sehr genau darauf achten wo man Patterns wie das oben beschriebene verwendet. Möchte man wirklich sicher gehen, dass bestimmte Aktionen auf einem wohldefinierten Datenstand ausgeführt werden, kann es besser sein, diese Aktionen nicht in einem Before Commit Event sondern im Microflow auszuführen in dem das Objekt committed wird. Idealerweise bevor ein Autocommit stattfinden kann.
Hi Andreas,
Perhaps the Java API isChanged() or getChangedMembers() on the Entity class do what you need?
I know that something like this exists. But I don’t think that it would show any changes, since the oject is already committed the moment I execute my action.
But there is an option in the Java API to check if an object is in the state „Autocommitted“. If so, the object was not existing before and can be handled as new object. You can use this java code to check it:
myIMendixObject.getState() == ObjectState.AUTOCOMMITTED
Thanks for the share.
question –
1. From the screen, I can see that you are committing the object with events on which Before Commit is called. Wouldn’t that lead to loop/overflow of some kind?
2. Say, we commit in the Before Commit microflow without event. Will there be another commit by the platform before the microflow is returned?
My before commit is not committing the object itself. It only commits a whole different object. That is OK, because there is no commit loop.
Why would you commit an object in it’s own before commit microflow? I don’t see the usecase. It will be committed after the before commit microflow eitherway.