- Type Parameters:
E- the entity type this callback applies to. UseEntity<?>to match all entity types.
The type parameter E determines which entity type this callback applies to. The framework automatically
resolves the type parameter at runtime and only invokes the callback for matching entity types. Use
EntityCallback<Entity<?>> to create a global callback that fires for all entities.
The "before" callbacks for insert, update, and upsert return the (potentially transformed) entity to persist,
which is essential for immutable record-based entities that cannot be mutated in place. The "after" callbacks and
beforeRemove(E) are observers that do not affect the persisted data.
Upsert callback routing
An upsert operation may be executed as a SQL-level upsert (e.g., INSERT ... ON CONFLICT,
MERGE), or it may be routed to a plain insert or update depending on the entity's primary key state and
the database dialect. The callbacks that fire depend on which path is taken:
- When routed to insert:
beforeInsert(E)/afterInsert(E)fire. - When routed to update:
beforeUpdate(E)/afterUpdate(E)fire. - When executed as a SQL-level upsert:
beforeUpsert(E)/afterUpsert(E)fire.
Exactly one pair of callbacks fires per entity; they are never combined.
"After" callback entity state
The "after" callbacks observe what the calling method reports to its caller:
- Methods that return nothing (
insert,update,upsert) report the entity as it was sent to the database, after the corresponding "before" transformation. No key is read back, so a database-generated primary key is not reflected. - The
*AndFetchIdmethods report that same entity carrying the primary key the database assigned. - The
*AndFetchmethods report the entity as read back from the database, reflecting generated keys, column defaults, version increments and trigger-applied changes.
A callback that needs the primary key must therefore be driven by a method that reports one. Selecting the method is the caller's choice: the callback receives exactly what the caller receives, and no more.
Batches
Each "after" callback has a form that takes a list, and Storm always calls that form: a write of several entities
passes the entities of one batch in the order they were written, and a write of one entity passes a list of one. By
default the list form calls the single-entity form for each entity, so a callback that overrides only the
single-entity form sees every entity. A callback that writes to the database itself overrides the list form instead,
so a batch costs it a fixed number of statements rather than a statement per entity. A *AndFetch call and a
write set pass each type's entities as one list once the rows have been read back, and a stream passes them a batch
at a time. Where several callbacks apply, each receives the whole list before the next one does.
Transactions
Callbacks run inline on the thread that performed the write and on its connection, so they see exactly the transaction the write runs in. The "before" callbacks run before the statement, the "after" callbacks once it has returned. Inside a transactional block, or under a transaction another framework manages, database work a callback performs belongs to that transaction and a rollback takes it back together with the write, and an "after" callback that throws rolls the write back with it.
Storm opens no transaction of its own. A write issued outside one commits on its own, and so does each statement
of a call that issues several, such as an *AndFetch method or a batch. An "after" callback then observes a
row that is already durable: throwing cannot take it back, and the callback's own statements commit separately. A
callback whose work has to succeed or fail with the write therefore depends on the caller having opened a
transaction.
A callback that performs database work of its own reaches its template through ORMTemplate.current(),
which returns the template of the operation that fired the callback, so the work goes to the same database, over
the same connection, inside the same transaction. A callback that captures a template instead makes that choice
once, at construction, and a callback registered on several templates has no correct choice to make. Callbacks
never fire recursively, so the work fires none of its own.
Because an "after" callback runs before the commit, it is the wrong place for an effect outside the database,
such as publishing an event or invalidating a cache: the transaction may still roll back, leaving the effect
describing a write that never landed. Such work belongs on a commit callback, which a callback registers by opening
a joining transactional block and calling Transaction.onCommit(Runnable); it runs once the physical
transaction has committed. Registering from the list form registers one commit callback for the batch rather than
one per entity. Commit callbacks run synchronously before the transactional block returns, so work that can block
for long belongs on a background worker the callback hands off to. They also run once the dispatch has returned,
where ORMTemplate.current() is no longer available, so a commit callback that performs database work reads
the template while the callback runs and captures it.
All methods have default no-op implementations, so users only need to override the hooks they care about.
Typical use cases include auditing (setting created/updated timestamps), validation, and logging.
- Since:
- 1.9
-
Method Summary
Modifier and TypeMethodDescriptiondefault voidafterInsert(E entity) Called after an entity has been successfully inserted into the database.default voidafterInsert(List<E> entities) Called after a batch of entities has been successfully inserted into the database.default voidafterRemove(E entity) Called after an entity has been successfully removed from the database.default voidafterRemove(List<E> entities) Called after a batch of entities has been successfully removed from the database.default voidafterUpdate(E entity) Called after an entity has been successfully updated in the database.default voidafterUpdate(List<E> entities) Called after a batch of entities has been successfully updated in the database.default voidafterUpsert(E entity) Called after an entity has been successfully upserted via a SQL-level upsert statement.default voidafterUpsert(List<E> entities) Called after a batch of entities has been successfully upserted via a SQL-level upsert statement.default EbeforeInsert(E entity) Called before an entity is inserted into the database.default voidbeforeRemove(E entity) Called before an entity is removed from the database.default EbeforeUpdate(E entity) Called before an entity is updated in the database.default EbeforeUpsert(E entity) Called before an entity is upserted via a SQL-level upsert statement (e.g.,INSERT ... ON CONFLICT,MERGE).
-
Method Details
-
beforeInsert
Called before an entity is inserted into the database.The returned entity is the one that will actually be persisted. Implementations may return a modified copy of the entity (e.g., with audit fields populated) or the original entity unchanged.
This callback also fires when an upsert operation is routed to an insert (e.g., for auto-generated primary keys on databases that cannot perform a SQL-level upsert with generated keys).
- Parameters:
entity- the entity about to be inserted; nevernull.- Returns:
- the entity to insert; never
null.
-
beforeUpdate
Called before an entity is updated in the database.The returned entity is the one that will actually be persisted. Implementations may return a modified copy of the entity (e.g., with an updated timestamp) or the original entity unchanged.
This callback also fires when an upsert operation is routed to an update (i.e., when the entity has an auto-generated primary key with a non-default value, indicating it was previously inserted).
- Parameters:
entity- the entity about to be updated; nevernull.- Returns:
- the entity to update; never
null.
-
afterInsert
Called after an entity has been successfully inserted into the database.The entity passed to this method reflects what the calling method reports: the entity as sent for
insert, the entity carrying its generated primary key forinsertAndFetchId(s), and the row as read back forinsertAndFetch.This callback also fires when an upsert operation is routed to an insert.
- Parameters:
entity- the entity that was inserted; nevernull.
-
afterInsert
Called after a batch of entities has been successfully inserted into the database.Storm delivers every insert through this method: a batch write passes the entities of one batch in insertion order, and a single-entity write passes a list of one. Each entity reflects what the calling method reports, as described for
afterInsert(Entity). Override this method to handle a batch as a whole, for instance with one statement for the batch whereafterInsert(Entity)would issue one per entity.This callback also fires when an upsert operation is routed to an insert, and receives upserted batches when the callback overrides neither
afterUpsert(Entity)norafterUpsert(List).By default, this calls
afterInsert(Entity)for each entity, in order.- Parameters:
entities- the entities that were inserted, in insertion order; nevernullor empty.- Since:
- 1.15
-
afterUpdate
Called after an entity has been successfully updated in the database.The entity passed to this method reflects what the calling method reports: the entity as sent for
update, and the row as read back forupdateAndFetch. Only the latter reflects database-side changes such as version increments or trigger-applied modifications.This callback also fires when an upsert operation is routed to an update.
- Parameters:
entity- the entity that was updated; nevernull.
-
afterUpdate
Called after a batch of entities has been successfully updated in the database.Storm delivers every update through this method: a batch write passes the entities of one batch in update order, and a single-entity write passes a list of one. Each entity reflects what the calling method reports, as described for
afterUpdate(Entity).By default, this calls
afterUpdate(Entity)for each entity, in order.- Parameters:
entities- the entities that were updated, in update order; nevernullor empty.- Since:
- 1.15
-
beforeUpsert
Called before an entity is upserted via a SQL-level upsert statement (e.g.,INSERT ... ON CONFLICT,MERGE).This callback only fires when the upsert is executed as a SQL-level upsert. When the operation is routed to a plain insert or update,
beforeInsert(E)orbeforeUpdate(E)fires instead.The returned entity is the one that will actually be persisted. By default, this delegates to
beforeInsert(Entity), so that insert callbacks automatically cover the upsert path. Override this method to provide upsert-specific behavior.- Parameters:
entity- the entity about to be upserted; nevernull.- Returns:
- the entity to upsert; never
null.
-
afterUpsert
Called after an entity has been successfully upserted via a SQL-level upsert statement.This callback only fires when the upsert is executed as a SQL-level upsert. When the operation is routed to a plain insert or update,
afterInsert(E)orafterUpdate(E)fires instead.The entity passed to this method reflects what the calling method reports: the entity as sent for
upsert, the entity carrying its generated primary key forupsertAndFetchId(s), and the row as read back forupsertAndFetch.By default, this delegates to
afterInsert(Entity), so that insert callbacks automatically cover the upsert path. Override this method to provide upsert-specific behavior.- Parameters:
entity- the entity that was upserted; nevernull.
-
afterUpsert
Called after a batch of entities has been successfully upserted via a SQL-level upsert statement.Storm delivers every SQL-level upsert through this method, or through
afterInsert(List)as described below: a batch write passes the entities of one batch in upsert order, and a single-entity write passes a list of one. Each entity reflects what the calling method reports, as described forafterUpsert(Entity).By default, this calls
afterUpsert(Entity)for each entity, in order. A callback that overrides neither this method norafterUpsert(Entity)receives upserted batches throughafterInsert(List)instead, so that insert callbacks, batched or not, cover the upsert path as the single-entity default does.- Parameters:
entities- the entities that were upserted, in upsert order; nevernullor empty.- Since:
- 1.15
-
beforeRemove
Called before an entity is removed from the database.Fires where the operation carries an entity, so
remove(entity)and its collection and stream forms trigger it.removeById,removeByRef,removeAlland thedelete()query builder identify rows by key or by predicate rather than by entity, so there is no entity to pass and this callback does not fire. A callback that throws in order to block a removal therefore blocks only the paths that carry an entity, and is not an enforcement point.- Parameters:
entity- the entity about to be removed; nevernull.
-
afterRemove
Called after an entity has been successfully removed from the database.As with
beforeRemove(E), fires only where the operation carries an entity.- Parameters:
entity- the entity that was removed; nevernull.
-
afterRemove
Called after a batch of entities has been successfully removed from the database.Storm delivers every removal that carries entities through this method: a batch removal passes the entities of one batch in removal order, and a single-entity removal passes a list of one. As with
afterRemove(Entity), removals by key or by predicate carry no entity and do not fire it.By default, this calls
afterRemove(Entity)for each entity, in order.- Parameters:
entities- the entities that were removed, in removal order; nevernullor empty.- Since:
- 1.15
-