Package st.orm

Interface WriteSet


public interface WriteSet
Dependency-aware write operations over mixed-type sets of entities.

A write set applies one write operation to a heterogeneous collection of entities. insert(Iterable) and upsert(Iterable) extend the explicit members (the entities supplied by the caller) with discovered members: unsaved entities transitively reachable through insertable, entity-valued foreign key fields. update(Iterable) and remove(Iterable) operate on the explicit members only. Each action accepts the entities as any Iterable or as varargs. The per-row semantics of each action are identical to the corresponding EntityRepository operation; the write set adds partitioning by type, dependency ordering and generated-key propagation:

  • Insert discovery. A record whose foreign key field holds an unsaved entity is a value that describes both rows; inserting the value inserts both. Discovery traverses entity-valued foreign key fields (including fields inside inline components) and entity-wrapped refs (see Ref.of(Entity)). Referenced entities that already carry a primary key are never discovered; unless they are explicit members themselves, they only provide foreign key values.
  • Ordering. A valid execution order is determined from the foreign key dependencies: parents-before-children for insert(Iterable) and upsert(Iterable), children-before-parents for remove(Iterable). update(Iterable) has no ordering constraints and is only grouped by type.
  • Key propagation. Generated primary keys propagate within the set by instance identity: a child links to its new parent by holding the same instance, either directly in the foreign key field or wrapped in a Ref. The same unsaved instance describes one prospective row; two structurally equal but distinct unsaved instances describe two rows. When a foreign key field is non-insertable because its column value is carried by a component of the primary key (the junction table pattern, where the key columns live inside a composite primary key), the generated key is written into the carrying key component instead.
  • Batching. Execution is grouped into one batch operation per entity type per dependency level (large batches are split by the configured batch size). The number of batches follows the dependency shape of the data: the Owner ← Pet ← Visit example below needs three, and a self-referencing type whose rows span several dependency levels needs one batch per level.

An entity is considered unsaved when its primary key is the default value and the primary key is auto-generated (identity or sequence). This test is local and deterministic; no session state, entity cache or database round trip is involved. There is no session-wide cascade or persistence context: all writes derive from the entities supplied to the call and, for insert and upsert, their discovered members.

A write set executes multiple statements and is not atomic by itself: when a later dependency level fails, the earlier levels have already been written. Run write sets inside a transaction when atomicity across the set is required.

Example, inserting a three-level graph with a shared new parent:


 var owner = new Owner("Alice", address);                    // unsaved
 var wolfie = new Pet("Wolfie", DOG, owner);                 // both pets share the owner instance
 var rex = new Pet("Rex", DOG, owner);
 var visit = new Visit(TODAY, "Check-up", wolfie);
 orm.writeSet().insert(wolfie, rex, visit);                  // owner joins via insert discovery:
                                                             // one Owner, one Pet and one Visit batch
 

Unsaved references that cannot be discovered fail fast with a descriptive exception before anything is written: an id-only Ref carrying a default id, an unsaved entity behind a non-insertable foreign key component whose column value is not carried by an insertable primary key component, an unsaved entity encountered by update(Iterable) or remove(Iterable), and dependency cycles that cannot be executed by the dependency-ordering strategy (the write set does not break cycles using nullable intermediate values, deferred constraints or follow-up updates).

Note on modified referenced entities: a keyed entity held in a foreign key field contributes exactly its primary key; modifications to it are not persisted by writing its dependent, by any action. One rule covers every action: a write set writes the entities named by the caller, plus the entities the values make necessary. An unsaved referenced entity is necessary (its dependent cannot be written without its key, and a row that does not exist cannot be a stale copy); a keyed referenced entity never is: it is the state that was hydrated when the value was read, and treating that snapshot as write intent would silently overwrite newer database state. To persist changes to a referenced entity, pass it as an explicit member.

Note on unsaved refs: Ref equality is based on type and id. Two refs wrapping distinct unsaved instances therefore compare equal until the instances are persisted. Do not use unsaved refs as map keys or set members; the write set itself correlates by instance identity and is not affected.

Note on entity callbacks: callbacks run inside the per-type repository operations, after the write set has discovered members and planned the execution order. A callback that alters foreign key fields does not change which entities are discovered or in which order they are written.

Since:
1.13
See Also:
  • Method Details

    • insert

      void insert(Iterable<? extends Entity<?>> entities)
      Inserts the explicit members and their discovered members, in dependency order.

      All explicit members are inserted with the exact semantics of the per-repository insert: auto-generated primary keys are assigned by the database (a preset value on an auto-generated key is ignored), and entities with non-generated keys are inserted with the key they carry. Unsaved entities reachable through insertable foreign key fields join the set as discovered members and are inserted before their dependents, with generated keys propagated by instance identity.

      Parameters:
      entities - the entities to insert; may span multiple entity types.
      Throws:
      PersistenceException - if the dependencies contain a cycle that cannot be ordered, if an unsaved entity is referenced through a non-insertable foreign key component, or if the insert fails.
    • insert

      default void insert(Entity<?>... entities)
      Inserts the given entities and their discovered members; see insert(Iterable). An empty call is a no-op.
      Parameters:
      entities - the entities to insert; may span multiple entity types.
      Throws:
      PersistenceException - if the dependencies contain a cycle that cannot be ordered, if an unsaved entity is referenced through a non-insertable foreign key component, or if the insert fails.
    • insertAndFetch

      List<Entity<?>> insertAndFetch(Iterable<? extends Entity<?>> entities)
      Inserts like insert(Iterable) and returns the explicit members as they exist in the database after the insert, in input order.

      The returned entities are re-fetched, so database-applied changes such as generated keys, defaults and version columns are reflected, and discovered members referenced by them are hydrated with their generated keys.

      Parameters:
      entities - the entities to insert; may span multiple entity types.
      Returns:
      the fetched entities in input order.
      Throws:
      PersistenceException - if the insert fails.
    • insertAndFetch

      default List<Entity<?>> insertAndFetch(Entity<?>... entities)
      Inserts like insertAndFetch(Iterable) and returns the explicit members as they exist in the database after the insert, in input order; an empty call is a no-op and returns an empty list.
      Parameters:
      entities - the entities to insert; may span multiple entity types.
      Returns:
      the fetched entities in input order.
      Throws:
      PersistenceException - if the insert fails.
    • insertAndFetchIds

      <ID> List<ID> insertAndFetchIds(Iterable<? extends Entity<ID>> entities)
      Inserts like insert(Iterable) and returns the primary keys of the explicit members, in input order.

      The keys are taken from the insert itself: generated keys as reported by the database, or the keys the entities carry when the primary key is not generated. The rows are not re-read, so database-applied defaults and version columns are not reflected; use insertAndFetch(Iterable) when that state is needed. Discovered members are inserted but not reported.

      The batch is homogeneous in its id type; entity types may differ as long as they share it. For batches that mix id types, use insertAndFetch(Iterable), where each returned entity carries its own id.

      Parameters:
      entities - the entities to insert; may span multiple entity types sharing the id type.
      Returns:
      the primary keys of the explicit members in input order.
      Throws:
      PersistenceException - if the dependencies contain a cycle that cannot be ordered, if an unsaved entity is referenced through a non-insertable foreign key component, or if the insert fails.
    • insertAndFetchId

      default <ID> ID insertAndFetchId(Entity<ID> entity)
      Inserts the given entity and its discovered members and returns its primary key; see insertAndFetchIds(Iterable).
      Parameters:
      entity - the entity to insert.
      Returns:
      the primary key of the inserted entity.
      Throws:
      PersistenceException - if the dependencies contain a cycle that cannot be ordered, if an unsaved entity is referenced through a non-insertable foreign key component, or if the insert fails.
    • update

      void update(Iterable<? extends Entity<?>> entities)
      Updates the given entities, grouped by type.

      Per-row semantics are identical to the per-repository update, including transaction-scoped dirty checking: entities that are unchanged compared to their observed state are skipped. Only the explicit members are updated; referenced entities are never updated implicitly, and there is no insert discovery — an unsaved explicit member is rejected (a row that does not exist cannot be updated), and an unsaved referenced entity fails where its key is required as a foreign key value.

      In particular, a modified referenced entity is not written: a keyed entity held in a foreign key field of a member contributes only its primary key, so its changes stay in memory. To persist changes to both a member and an entity it references, pass both as explicit members; dirty checking skips whichever members are unchanged.

      Parameters:
      entities - the entities to update; may span multiple entity types.
      Throws:
      PersistenceException - if an explicit member or a referenced entity is unsaved, or if the update fails.
    • update

      default void update(Entity<?>... entities)
      Updates the given entities; see update(Iterable). An empty call is a no-op.
      Parameters:
      entities - the entities to update; may span multiple entity types.
      Throws:
      PersistenceException - if an explicit member or a referenced entity is unsaved, or if the update fails.
    • updateAndFetch

      List<Entity<?>> updateAndFetch(Iterable<? extends Entity<?>> entities)
      Updates like update(Iterable) and returns the passed entities as they exist in the database after the update, in input order.
      Parameters:
      entities - the entities to update; may span multiple entity types.
      Returns:
      the fetched entities in input order.
      Throws:
      PersistenceException - if an explicit member or a referenced entity is unsaved, or if the update fails.
    • updateAndFetch

      default List<Entity<?>> updateAndFetch(Entity<?>... entities)
      Updates like updateAndFetch(Iterable) and returns the passed entities as they exist in the database after the update, in input order; an empty call is a no-op and returns an empty list.
      Parameters:
      entities - the entities to update; may span multiple entity types.
      Returns:
      the fetched entities in input order.
      Throws:
      PersistenceException - if an explicit member or a referenced entity is unsaved, or if the update fails.
    • upsert

      void upsert(Iterable<? extends Entity<?>> entities)
      Upserts the explicit members and inserts their discovered members, in dependency order.

      Explicit members are upserted with the exact semantics of the per-repository upsert (native ON CONFLICT / MERGE where available); explicit membership takes precedence, so a keyed entity that is both supplied and referenced by another member is upserted, and is written before its dependents. Unsaved entities reachable through insertable foreign key fields join the set as discovered members and are inserted before their dependents, with generated keys propagated by instance identity. Keyed referenced entities that are not explicit members only provide foreign key values; modifications to them are not persisted.

      Parameters:
      entities - the entities to upsert; may span multiple entity types.
      Throws:
      PersistenceException - if the dependencies contain a cycle that cannot be ordered, if an unsaved entity is referenced through a non-insertable foreign key component, or if the upsert fails.
    • upsert

      default void upsert(Entity<?>... entities)
      Upserts the given entities and inserts their discovered members; see upsert(Iterable). An empty call is a no-op.
      Parameters:
      entities - the entities to upsert; may span multiple entity types.
      Throws:
      PersistenceException - if the dependencies contain a cycle that cannot be ordered, if an unsaved entity is referenced through a non-insertable foreign key component, or if the upsert fails.
    • upsertAndFetch

      List<Entity<?>> upsertAndFetch(Iterable<? extends Entity<?>> entities)
      Upserts like upsert(Iterable) and returns the passed entities as they exist in the database after the upsert, in input order.
      Parameters:
      entities - the entities to upsert; may span multiple entity types.
      Returns:
      the fetched entities in input order.
      Throws:
      PersistenceException - if the upsert fails.
    • upsertAndFetch

      default List<Entity<?>> upsertAndFetch(Entity<?>... entities)
      Upserts like upsertAndFetch(Iterable) and returns the passed entities as they exist in the database after the upsert, in input order; an empty call is a no-op and returns an empty list.
      Parameters:
      entities - the entities to upsert; may span multiple entity types.
      Returns:
      the fetched entities in input order.
      Throws:
      PersistenceException - if the upsert fails.
    • upsertAndFetchIds

      <ID> List<ID> upsertAndFetchIds(Iterable<? extends Entity<ID>> entities)
      Upserts like upsert(Iterable) and returns the primary keys of the explicit members, in input order.

      For inserted rows the generated key is reported; for updated rows the key the entity carries. The rows are not re-read, so database-applied defaults and version columns are not reflected; use upsertAndFetch(Iterable) when that state is needed. Discovered members are written but not reported.

      The batch is homogeneous in its id type; entity types may differ as long as they share it. For batches that mix id types, use upsertAndFetch(Iterable), where each returned entity carries its own id.

      Parameters:
      entities - the entities to upsert; may span multiple entity types sharing the id type.
      Returns:
      the primary keys of the explicit members in input order.
      Throws:
      PersistenceException - if the dependencies contain a cycle that cannot be ordered, if an unsaved entity is referenced through a non-insertable foreign key component, or if the upsert fails.
    • upsertAndFetchId

      default <ID> ID upsertAndFetchId(Entity<ID> entity)
      Upserts the given entity and its discovered members and returns its primary key; see upsertAndFetchIds(Iterable).
      Parameters:
      entity - the entity to upsert.
      Returns:
      the primary key of the upserted entity.
      Throws:
      PersistenceException - if the dependencies contain a cycle that cannot be ordered, if an unsaved entity is referenced through a non-insertable foreign key component, or if the upsert fails.
    • remove

      void remove(Iterable<? extends Entity<?>> entities)
      Removes the given entities, children before parents.

      Only the explicit members are removed; referenced entities are never removed implicitly. Dependencies between set members are resolved by entity type and primary key (not instance identity), so a member referencing another member through a foreign key is removed first, regardless of whether the two hold the same instance. Unsaved entities are rejected.

      Parameters:
      entities - the entities to remove; may span multiple entity types.
      Throws:
      PersistenceException - if a passed entity is unsaved, or if the removal fails.
    • remove

      default void remove(Entity<?>... entities)
      Removes the given entities, children before parents; see remove(Iterable). An empty call is a no-op.
      Parameters:
      entities - the entities to remove; may span multiple entity types.
      Throws:
      PersistenceException - if a passed entity is unsaved, or if the removal fails.
    • insert

      default void insert(Entity<?> entity)
      Inserts the given entity and its discovered members; see insert(Iterable).
      Parameters:
      entity - the root entity to insert.
      Throws:
      PersistenceException - if the insert fails.
    • insertAndFetch

      default <E extends Entity<?>> E insertAndFetch(E entity)
      Inserts the given entity and its discovered members, and returns the entity as it exists in the database after the insert; see insertAndFetch(Iterable).
      Type Parameters:
      E - the entity type.
      Parameters:
      entity - the root entity to insert.
      Returns:
      the fetched entity, with generated keys, defaults and version columns reflected and discovered members hydrated.
      Throws:
      PersistenceException - if the insert fails.
    • update

      default void update(Entity<?> entity)
      Updates the given entity; see update(Iterable).
      Parameters:
      entity - the entity to update.
      Throws:
      PersistenceException - if the entity is unsaved or the update fails.
    • updateAndFetch

      default <E extends Entity<?>> E updateAndFetch(E entity)
      Updates the given entity and returns it as it exists in the database after the update; see updateAndFetch(Iterable).
      Type Parameters:
      E - the entity type.
      Parameters:
      entity - the entity to update.
      Returns:
      the fetched entity.
      Throws:
      PersistenceException - if the entity is unsaved or the update fails.
    • upsert

      default void upsert(Entity<?> entity)
      Upserts the given entity and inserts its discovered members; see upsert(Iterable).
      Parameters:
      entity - the root entity to upsert.
      Throws:
      PersistenceException - if the upsert fails.
    • upsertAndFetch

      default <E extends Entity<?>> E upsertAndFetch(E entity)
      Upserts the given entity, inserts its discovered members, and returns the entity as it exists in the database after the upsert; see upsertAndFetch(Iterable).
      Type Parameters:
      E - the entity type.
      Parameters:
      entity - the root entity to upsert.
      Returns:
      the fetched entity.
      Throws:
      PersistenceException - if the upsert fails.
    • remove

      default void remove(Entity<?> entity)
      Removes the given entity; see remove(Iterable).
      Parameters:
      entity - the entity to remove.
      Throws:
      PersistenceException - if the entity is unsaved or the removal fails.