attach

suspend fun attach(entity: Entity<ID>, force: Boolean = false)

R2DBC-specific helper. Registers an entity from a previous transaction in the current transaction's cache, allowing it to be read and modified in the new transaction.

In JDBC DAO, Column.setValue can synchronously query the database and implicitly adopt an entity from another transaction. R2DBC's setValue is non-suspend, so this must be done explicitly before mutating the entity.

Behavior:

  • If entity is already the instance this transaction tracks, does nothing.

  • If nothing is tracked for that id, verifies that the row still exists in the database (throws EntityNotFoundException if not) and stores entity.

  • If a different instance of the same row is tracked, entity replaces it, so that subsequent property reads and writes go through the caller's instance. Two instances of one row arise routinely — for example when this transaction also loads the row itself — and the replaced instance is only a cache entry, so discarding it is harmless. Unless it has unflushed changes, in which case replacing it would silently drop them and this throws instead; pass force to discard them deliberately.

Flush behavior: After attaching and modifying an entity, there is no need to call flush() explicitly — pending changes are flushed by EntityLifecycleInterceptor.beforeCommit as part of the transaction's commit. This is consistent with JDBC behavior.

Typical usage:

val entity = suspendTransaction { MyEntity.newSuspend { name = "foo" } }
suspendTransaction {
MyEntity.attach(entity) // register in new transaction's cache
entity.name = "bar" // now safe to modify
}

Parameters

entity

The entity to track in the current transaction.

force

Replace a tracked instance even when it holds unflushed changes, discarding them.

Throws

if the row no longer exists in the database.

if another instance of the same row is tracked with unflushed changes and force is false.