Package st.orm

Interface EntityCallback<E extends Entity<?>>

Type Parameters:
E - the entity type this callback applies to. Use Entity<?> to match all entity types.

public interface EntityCallback<E extends Entity<?>>
Typed callback interface for entity lifecycle events.

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:

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 *AndFetchId methods report that same entity carrying the primary key the database assigned.
  • The *AndFetch methods 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 Type
    Method
    Description
    default void
    afterInsert(E entity)
    Called after an entity has been successfully inserted into the database.
    default void
    afterInsert(List<E> entities)
    Called after a batch of entities has been successfully inserted into the database.
    default void
    afterRemove(E entity)
    Called after an entity has been successfully removed from the database.
    default void
    afterRemove(List<E> entities)
    Called after a batch of entities has been successfully removed from the database.
    default void
    afterUpdate(E entity)
    Called after an entity has been successfully updated in the database.
    default void
    afterUpdate(List<E> entities)
    Called after a batch of entities has been successfully updated in the database.
    default void
    afterUpsert(E entity)
    Called after an entity has been successfully upserted via a SQL-level upsert statement.
    default void
    afterUpsert(List<E> entities)
    Called after a batch of entities has been successfully upserted via a SQL-level upsert statement.
    default E
    beforeInsert(E entity)
    Called before an entity is inserted into the database.
    default void
    beforeRemove(E entity)
    Called before an entity is removed from the database.
    default E
    beforeUpdate(E entity)
    Called before an entity is updated in the database.
    default E
    beforeUpsert(E entity)
    Called before an entity is upserted via a SQL-level upsert statement (e.g., INSERT ... ON CONFLICT, MERGE).
  • Method Details

    • beforeInsert

      default E beforeInsert(E entity)
      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; never null.
      Returns:
      the entity to insert; never null.
    • beforeUpdate

      default E beforeUpdate(E entity)
      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; never null.
      Returns:
      the entity to update; never null.
    • afterInsert

      default void afterInsert(E entity)
      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 for insertAndFetchId(s), and the row as read back for insertAndFetch.

      This callback also fires when an upsert operation is routed to an insert.

      Parameters:
      entity - the entity that was inserted; never null.
    • afterInsert

      default void afterInsert(List<E> entities)
      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 where afterInsert(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) nor afterUpsert(List).

      By default, this calls afterInsert(Entity) for each entity, in order.

      Parameters:
      entities - the entities that were inserted, in insertion order; never null or empty.
      Since:
      1.15
    • afterUpdate

      default void afterUpdate(E entity)
      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 for updateAndFetch. 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; never null.
    • afterUpdate

      default void afterUpdate(List<E> entities)
      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; never null or empty.
      Since:
      1.15
    • beforeUpsert

      default E beforeUpsert(E entity)
      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) or beforeUpdate(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; never null.
      Returns:
      the entity to upsert; never null.
    • afterUpsert

      default void afterUpsert(E entity)
      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) or afterUpdate(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 for upsertAndFetchId(s), and the row as read back for upsertAndFetch.

      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; never null.
    • afterUpsert

      default void afterUpsert(List<E> entities)
      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 for afterUpsert(Entity).

      By default, this calls afterUpsert(Entity) for each entity, in order. A callback that overrides neither this method nor afterUpsert(Entity) receives upserted batches through afterInsert(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; never null or empty.
      Since:
      1.15
    • beforeRemove

      default void beforeRemove(E entity)
      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, removeAll and the delete() 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; never null.
    • afterRemove

      default void afterRemove(E entity)
      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; never null.
    • afterRemove

      default void afterRemove(List<E> entities)
      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; never null or empty.
      Since:
      1.15