What this is about
A hook is a class in your project that CDMS calls at fixed points of a standard flow. This is how you plug in your own subject-area logic without touching generated code: assign a customer number, check a business rule, decrypt a field for the response.
This page describes the model hook. It always belongs to one model. CDMS calls it for every object of this model that a request creates, changes, deletes, rolls back or reads. It also tells the hook which operation is running. Logic for a single value that you need on many fields, such as encrypting, is a field hook.
Writing and registering a hook
A hook is a Spring bean that implements HookServiceInterface<…Entity> for the entity of the model:
@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
}
}
}
-
1Developerwrites the class under the base package of the project and makes it a bean with
@ServiceSpring only finds beans undercom.codamai. The base package from the project scaffold lies there. -
2Developerputs
@Orderdirectly on the class when the order of several hooks matters -
3CDMSon the first object of a model, looks up all beans that implement
HookServiceInterfacefor exactly this entity -
4CDMSsorts them by
@Orderand remembers the list -
5Hookis called from now on for every object of this model
The interface has exactly two methods: beforeDatabaseChange and afterDatabaseChange.
In the hub you can add a hook to a model. This entry only announces the logic. What actually runs is decided by the class in your project alone. Where else custom code belongs is described in Generated code and custom code.
A hook applies to the entity in its type parameter. With abstract models, CDMS always works with the concrete subtype, so the hook of the subtype runs, for example PrivatkundeEntity.
The operations
The second parameter method tells you what is happening. A hook receives all operations and decides by itself which ones it reacts to.
CmsMethods | triggered by | before | after |
|---|---|---|---|
CREATE | create, also every new child in a nested write | yes | yes |
UPDATE | PUT, also every child that is changed along through a relation with UPDATE | yes | yes |
PATCH | PATCH, also every child that is changed along | yes | yes |
DELETE | delete, also every dependent child that is deleted along | yes | yes |
ROLLBACK | rolling back to an earlier revision | yes | yes |
READ | read by id, search, expanded references, read-back after a write | – | yes |
A search also reports itself as READ, just like reading a single object.
The timeline of a write operation
sequenceDiagram
participant C as Client
participant D as CDMS
participant H as Hook
participant DB as Database
C->>D: POST /order/create
D->>D: check roles, transfer fields, collect violations
D->>H: before (CREATE)
Note over H: may still change the object
D->>D: check violations again → otherwise 422
D->>DB: hand over the object, the id is assigned
D->>H: after (CREATE)
D->>DB: flush: execute SQL
D->>DB: read back
D->>H: after (READ) for every object read
D->>DB: commit
D-->>C: 200 with the object
So a write operation usually triggers two kinds of hooks: those of the write operation and then READ when reading back for the response. Only DELETE reads nothing back. The steps in detail are in The order within a write operation.
before and after
beforeDatabaseChange- runs after CDMS has transferred all fields, before validation
- new objects have no
idyet - with PUT and PATCH the new values are already on the object
- changes to the object are saved, and still checked if the field had a violation before
- typical: derive values, check business rules, reject
afterDatabaseChange- runs after CDMS has handed the object to the database, before the flush and before the commit
- new objects have their
id - changes to the object are still saved, but no longer validated
- typical: prepare follow-up actions that need the
id - with
READ: adjust values for the response only
Both points lie inside the transaction. If a hook throws an error, the whole request is rolled back. What reaches the client then is described in When a hook fails.
All operations in detail
When: POST /create, or a new child without id in a nested write
-
1Hookbefore: the new object with the sent values and the default values, without
id -
2Hookafter: the same object, now with
id
Result: A before hook can fill a required field that the client does not even know, for example an order number. See Creating an object.
When: PUT /update/{id}, or a child with id that is changed along through a relation with UPDATE
-
1Hookbefore: the stored object, into which CDMS has already transferred the sent values
-
2Hookafter: the same object after it was handed to the database
Result: You no longer see the old value of a field on the object. See Replacing with PUT.
When: PATCH /update/{id}, or a child that is changed along
-
1Hookbefore: the stored object with the changed fields
-
2Hookafter: the same object after it was handed to the database
Result: The operation is called PATCH, not UPDATE. If your hook should run on every change, check for both. See Changing with PATCH.
When: DELETE /delete/{id}, every dependent child deleted along, and every dependent child that drops out of its parent through PUT or PATCH
-
1Hookbefore: the object with its fields, before it disappears from the databaseIf the hook throws an error, everything stays as it was.
-
2Hookafter: the same object, handed over for removal
Result: See How a DELETE runs.
When: POST {basis}/{id}/rollback/{revision}, only for the addressed object
-
1Hookbefore: the object in its current state
-
2CDMS→Databaserestores the revision
-
3Hookafter: the object in its restored state
Result: An operation of its own, so that your hook can tell a rollback from an ordinary change. See Rolling back to an old state.
When: every object CDMS loads from the database: read by id, every row of a search, every expanded reference, the read-back after a write
-
1CDMS→Databaseloads the requested fields
-
2Hookafter: the loaded object, before it becomes the responseThe object only contains the fields the request asked for. All others are empty.
-
3CDMS→Clientresponse with the values the hook left
Result: What the hook changes here only ends up in the response, not in the database. The history of an object does not pass through the READ hook. See Reading an object.
Several hooks on one model: @Order
You may write several hooks for one model, for example one for assigning numbers and one for a business check. @Order sets their order: smaller number first.
-
1Hook
@Order(10)NumberHook: assigns the order number -
2Hook
@Order(20)CheckHook: checks whether the order fits the customer, and already sees the number
The order applies per object: for each object, all hooks of its model run one after the other, then the next object follows. The order in which the objects of a nested write come up is described in Hooks for nested objects and cascades.
Decision table
| What you want to do | Hook point |
|---|---|
| derive a field or fill a required field | before, CREATE (and UPDATE/PATCH if it can change) |
| check a business rule and reject | before, with an exception |
reuse the new id | after, CREATE |
| prevent something from being deleted | before, DELETE, with an exception |
| convert a value for the response only | after, READ |
| store a value encrypted, or treat it the same way on many fields | field hook |
| send an email as soon as the change is safely saved | no hook point lies after the commit, see pitfalls |
Pitfalls
Where to go next
- Where the hooks sit between role check, validation and saving: The order within a write operation
- Hooks for children and objects deleted along: Hooks for nested objects and cascades
- What the client sees when a hook throws: When a hook fails
- Logic for a single value, such as encrypting or deriving: Field hooks