Worum es geht
Ein Hook (englisch für „Haken“) ist eine Klasse in deinem Projekt, die CDMS an festen Stellen eines Standardablaufs aufruft. So hängst du eigene Fachlogik ein, ohne generierten Code anzufassen: eine Kundennummer vergeben, eine fachliche Regel prüfen, ein Feld für die Antwort entschlüsseln.
Diese Seite beschreibt den Modell-Hook. Er gehört immer zu einem Modell. CDMS ruft ihn für jedes Objekt dieses Modells auf, das eine Anfrage anlegt, ändert, löscht, zurücksetzt oder liest. Dabei sagt es dem Hook, welche Operation gerade läuft. Logik für einen einzelnen Wert, die du an vielen Feldern brauchst, etwa Verschlüsseln, ist ein Feld-Hook.
Einen Hook schreiben und registrieren
Ein Hook ist eine Spring-Bean, die HookServiceInterface<…Entity> für die Entity des Modells implementiert:
@Service
@Order(100)
public class OrderHook implements HookServiceInterface<OrderEntity> {
@Override
public void beforeDatabaseChange(OrderEntity item, CmsMethods method)
throws AbstractCodamaiException {
if (method == CmsMethods.CREATE && item.getOrderNr() == null) {
item.setOrderNr(nextNumber());
}
}
@Override
public void afterDatabaseChange(OrderEntity item, CmsMethods method)
throws AbstractCodamaiException {
if (method == CmsMethods.READ) {
// e.g. convert a value for the response
}
}
}
-
1Entwicklerschreibt die Klasse unter dem Basispaket des Projekts und macht sie mit
@Servicezur BeanSpring findet nur Beans untercom.codamai. Das Basispaket aus dem Projektrahmen liegt dort. -
2Entwicklersetzt
@Orderdirekt an die Klasse, wenn die Reihenfolge mehrerer Hooks zählt -
3CDMSsucht beim ersten Objekt eines Modells alle Beans, die
HookServiceInterfacefür genau diese Entity implementieren -
4CDMSsortiert sie nach
@Orderund merkt sich die Liste -
5Hookwird ab jetzt für jedes Objekt dieses Modells aufgerufen
Das Interface hat genau zwei Methoden: beforeDatabaseChange und afterDatabaseChange.
Im Hub kannst du einen Hook am Modell eintragen. Dieser Eintrag kündigt die Logik nur an. Was wirklich läuft, bestimmt allein die Klasse in deinem Projekt. Wo eigener Code sonst noch hingehört, steht unter Generierter Code und eigener Code.
Ein Hook gilt für die Entity in seinem Typparameter. Bei abstrakten Modellen arbeitet CDMS immer mit dem konkreten Untertyp, also läuft der Hook des Untertyps, etwa PrivatkundeEntity.
Die Operationen
Der zweite Parameter method sagt dir, was gerade passiert. Ein Hook bekommt alle Operationen und entscheidet selbst, auf welche er reagiert.
CmsMethods | ausgelöst durch | before | after |
|---|---|---|---|
CREATE | Anlegen, auch jedes neue Kind in einem verschachtelten Schreibvorgang | ja | ja |
UPDATE | PUT, auch jedes Kind, das über eine Beziehung mit UPDATE mitgeändert wird | ja | ja |
PATCH | PATCH, auch jedes Kind, das dabei mitgeändert wird | ja | ja |
DELETE | Löschen, auch jedes abhängige Kind, das mitgelöscht wird | ja | ja |
ROLLBACK | Zurücksetzen auf eine frühere Revision | ja | ja |
READ | Lesen per id, Suche, ausgebaute Referenzen, Zurücklesen nach dem Schreiben | – | ja |
Eine Suche meldet sich ebenfalls als READ, genau wie das Lesen eines einzelnen Objekts.
Die Zeitachse eines Schreibvorgangs
sequenceDiagram
participant C as Client
participant D as CDMS
participant H as Hook
participant DB as Datenbank
C->>D: POST /order/create
D->>D: Rollen prüfen, Felder übertragen, Verstöße sammeln
D->>H: before (CREATE)
Note over H: darf das Objekt noch ändern
D->>D: Verstöße erneut prüfen → sonst 422
D->>DB: Objekt übergeben, die id wird vergeben
D->>H: after (CREATE)
D->>DB: flush: SQL ausführen
D->>DB: zurücklesen
D->>H: after (READ) für jedes gelesene Objekt
D->>DB: commit
D-->>C: 200 mit dem Objekt
Ein Schreibvorgang löst also meist zwei Arten von Hooks aus: die der Schreib-Operation und danach READ beim Zurücklesen für die Antwort. Nur DELETE liest nichts zurück. Die Schritte im Detail stehen unter Die Reihenfolge in einem Schreibvorgang.
before und after
beforeDatabaseChange- läuft, nachdem CDMS alle Felder übertragen hat, vor der Validierung
- neue Objekte haben noch keine
id - bei PUT und PATCH stehen schon die neuen Werte am Objekt
- Änderungen am Objekt werden gespeichert und noch geprüft, wenn das Feld vorher einen Verstoß hatte
- typisch: Werte ableiten, fachlich prüfen, ablehnen
afterDatabaseChange- läuft, nachdem CDMS das Objekt an die Datenbank übergeben hat, vor dem flush und vor dem Commit
- neue Objekte haben ihre
id - Änderungen am Objekt werden noch gespeichert, aber nicht mehr validiert
- typisch: Folgeaktionen vorbereiten, die die
idbrauchen - bei
READ: Werte nur für die Antwort anpassen
Beide Punkte liegen in der Transaktion. Wirft ein Hook einen Fehler, wird die ganze Anfrage zurückgerollt. Was dann beim Client ankommt, steht unter Wenn ein Hook scheitert.
Alle Operationen im Einzelnen
Wann: POST /create, oder ein neues Kind ohne id in einem verschachtelten Schreibvorgang
-
1Hookbefore: das neue Objekt mit den gesendeten Werten und den Defaultwerten, ohne
id -
2Hookafter: dasselbe Objekt, jetzt mit
id
Ergebnis: Ein before-Hook kann ein Pflichtfeld füllen, das der Client gar nicht kennt, etwa eine Auftragsnummer. Siehe Ein Objekt anlegen.
Wann: PUT /update/{id}, oder ein Kind mit id, das über eine Beziehung mit UPDATE mitgeändert wird
-
1Hookbefore: das gespeicherte Objekt, in das CDMS die gesendeten Werte schon übertragen hat
-
2Hookafter: dasselbe Objekt nach der Übergabe an die Datenbank
Ergebnis: Den alten Wert eines Feldes siehst du am Objekt nicht mehr. Siehe Ersetzen mit PUT.
Wann: PATCH /update/{id}, oder ein Kind, das dabei mitgeändert wird
-
1Hookbefore: das gespeicherte Objekt mit den geänderten Feldern
-
2Hookafter: dasselbe Objekt nach der Übergabe an die Datenbank
Ergebnis: Die Operation heißt PATCH, nicht UPDATE. Soll dein Hook bei jeder Änderung laufen, prüfe auf beide. Siehe Ändern mit PATCH.
Wann: DELETE /delete/{id}, jedes mitgelöschte abhängige Kind, und jedes abhängige Kind, das durch PUT oder PATCH aus seinem Elternobjekt fällt
-
1Hookbefore: das Objekt mit seinen Feldern, bevor es aus der Datenbank verschwindetWirft der Hook einen Fehler, bleibt alles, wie es war.
-
2Hookafter: dasselbe Objekt, zum Entfernen übergeben
Ergebnis: Siehe Der Ablauf eines DELETE.
Wann: POST {basis}/{id}/rollback/{revision}, nur für das angesprochene Objekt
-
1Hookbefore: das Objekt im aktuellen Stand
-
2CDMS→Databasestellt die Revision wieder her
-
3Hookafter: das Objekt im wiederhergestellten Stand
Ergebnis: Eine eigene Operation, damit dein Hook ein Zurücksetzen von einer normalen Änderung unterscheiden kann. Siehe Auf einen alten Stand zurücksetzen.
Wann: jedes Objekt, das CDMS aus der Datenbank lädt: Lesen per id, jede Zeile einer Suche, jede ausgebaute Referenz, das Zurücklesen nach dem Schreiben
-
1CDMS→Databaselädt die angeforderten Felder
-
2Hookafter: das geladene Objekt, bevor es zur Antwort wirdDas Objekt enthält nur die Felder, die die Anfrage angefordert hat. Alle anderen sind leer.
-
3CDMS→ClientAntwort mit den Werten, die der Hook hinterlassen hat
Ergebnis: Was der Hook hier ändert, landet nur in der Antwort, nicht in der Datenbank. Die Historie eines Objekts läuft nicht durch den READ-Hook. Siehe Ein Objekt lesen.
Mehrere Hooks auf einem Modell: @Order
Du darfst für ein Modell mehrere Hooks schreiben, etwa einen für die Nummernvergabe und einen für eine fachliche Prüfung. @Order legt ihre Reihenfolge fest: kleinere Zahl zuerst.
-
1Hook
@Order(10)NumberHook: vergibt die Auftragsnummer -
2Hook
@Order(20)CheckHook: prüft, ob der Auftrag zum Kunden passt, und sieht die Nummer schon
Die Reihenfolge gilt je Objekt: Für jedes Objekt laufen alle Hooks seines Modells nacheinander, dann kommt das nächste Objekt dran. In welcher Reihenfolge die Objekte eines verschachtelten Schreibvorgangs drankommen, steht unter Hooks bei verschachtelten Objekten und Kaskaden.
Entscheidungstabelle
| Was du tun willst | Hook-Punkt |
|---|---|
| ein Feld ableiten oder ein Pflichtfeld füllen | before, CREATE (und UPDATE/PATCH, wenn es sich ändern kann) |
| eine fachliche Regel prüfen und ablehnen | before, mit einer Exception |
die neue id weiterverwenden | after, CREATE |
| verhindern, dass etwas gelöscht wird | before, DELETE, mit einer Exception |
| einen Wert nur für die Antwort umrechnen | after, READ |
| einen Wert verschlüsselt speichern oder an vielen Feldern gleich behandeln | Feld-Hook |
| eine E-Mail schicken, sobald die Änderung sicher gespeichert ist | kein Hook-Punkt liegt nach dem Commit, siehe Fallen |
Fallen
Wie es weitergeht
- Wo die Hooks zwischen Rollenprüfung, Validierung und Speichern liegen: Die Reihenfolge in einem Schreibvorgang
- Hooks für Kinder und mitgelöschte Objekte: Hooks bei verschachtelten Objekten und Kaskaden
- Was der Client sieht, wenn ein Hook wirft: Wenn ein Hook scheitert
- Logik für einen einzelnen Wert, etwa Verschlüsseln oder Ausrechnen: Feld-Hooks