Generated Methods
Every @DbEntity class gets one generated repository. This page documents every method on the generated <Entity>Repository class.
Repository signature
The generated class is concrete (not an interface). Inject the SqlDriver directly — see Initialization.
createTable()
Creates the table if it does not already exist. Call once per repository during app startup, before any other method.
- Uses
CREATE TABLE IF NOT EXISTS— safe to call more than once. - Delegates to
SchemaMigrator.sync()— adds, renames, removes, or recreates columns when the entity changes. See Auto-migration.
insert(entity: T)
Inserts a single row. Binds all non-@Ignore properties as ? parameters.
Auto-generated primary key: pass id = 0 (or whatever the Kotlin default is). SQLite assigns the real id.
Note
insert does not return the generated id. Query by a unique field if you need the assigned id immediately.
update(entity: T)
Updates the row whose primary key matches entity.<pkProperty>. All non-PK, non-@Ignore columns are set to the entity's current values.
If no row with that primary key exists, the statement is a no-op (zero rows affected, no error).
delete(id: ID)
Deletes the row with the given primary key. ID is the Kotlin type of the @PrimaryKey property.
If no row exists with that id, the statement is a no-op.
deleteWhere { predicate }
Deletes all rows matching the DSL predicate. Returns the number of rows deleted.
val deleted = taskRepo.deleteWhere {
(TaskColumns.projectId eq projectId) and
(TaskColumns.isCompleted eq true)
}
See DSL Operators for the full predicate DSL.
findById(id: ID): T?
Returns the matching row, or null if not found. Never throws for a missing row.
findAll(): List<T>
Returns all rows in insertion order. Returns an empty list (not null) when the table is empty.
findWhere { predicate }: List<T>
Returns all rows matching the DSL predicate. Returns an empty list when no rows match.
observeAll(): Flow<List<T>>
Returns a cold Flow that emits the full table on collection and re-emits on every subsequent write (insert, update, delete, deleteWhere) to the same table.
Uses SQLDelight Query.Listener internally — no polling.
observeWhere { predicate }: Flow<List<T>>
Like observeAll(), but filters by the DSL predicate on every emission.
val activeTasks: Flow<List<Task>> = taskRepo.observeWhere {
TaskColumns.status inList listOf("TODO", "IN_PROGRESS")
}
Note
The predicate is re-evaluated on every emission — not just the first. Changes to the underlying data that affect the predicate result in an updated list.
count(): Long
Returns the total number of rows in the table.
count { predicate }: Long
Returns the number of rows matching the predicate.
val openCount: Long = taskRepo.count {
(TaskColumns.projectId eq projectId) and
(TaskColumns.isCompleted eq false)
}
insertAll(entities: List<T>)
Batch-inserts a list of entities inside a single transaction. Reactive observers receive one Flow emission after all rows are committed rather than one per insert call.
Internally calls driver.withTransaction { entities.forEach { insert(it) } }. If any insert fails, the entire batch is rolled back.
findBy<Parent>(parentId: PK): List<T>
Generated for each @Relation-annotated foreign-key property. Returns all child rows whose FK column equals parentId.
// Task has: @Relation val projectId: Long
val tasks: List<Task> = taskRepo.findByProject(projectId)
observeBy<Parent>(parentId: PK): Flow<List<T>>
Reactive variant of findBy<Parent>. Re-emits whenever the child table changes.
deleteBy<Parent>(parentId: PK)
Deletes all child rows whose FK column equals parentId. Used for cascade deletes.
// Remove all tasks before deleting the parent project
taskRepo.deleteByProject(projectId)
projectRepo.delete(projectId)
For cleaner cascade semantics, wrap both calls in driver.withTransaction { … } so both tables are notified in a single Flow emission.
Transactions
driver.withTransaction { }
suspend fun SqlDriver.withTransaction(
context: CoroutineContext = Dispatchers.Default,
block: suspend () -> Unit
)
Executes block inside a BEGIN TRANSACTION … COMMIT. On any exception the transaction is rolled back and the exception re-thrown.
All Kiln repository write methods (insert, update, delete, deleteWhere, insertAll) defer their Flow listener notifications when called inside withTransaction. After a successful commit, each affected table is notified exactly once — reactive Flows receive one emission for the whole transaction rather than one per operation.
No notifications are sent when a transaction is rolled back.
// Cascade delete — observers see one emission each, not four
driver.withTransaction {
taskRepo.deleteByProject(projectId) // deferred
projectRepo.delete(projectId) // deferred
}
// notifyListeners fires here, once per table
// Batch import — observers see all rows appear atomically
driver.withTransaction {
taskRepo.insertAll(newTasks)
}
driver.notifyOrDefer(tableName)
Called automatically by generated repository code. Outside a transaction it calls SqlDriver.notifyListeners immediately. Inside a withTransaction block it adds the table name to the transaction's dirty set, deferring the notification to after commit. Not intended for direct use.
Companion: <Entity>Columns
Each entity also generates a companion <Entity>Columns object used inside DSL lambdas:
object ProductColumns {
val id: Column<String>
val name: Column<String>
val price: Column<Double>
val inStock: Column<Boolean>
}
Column names reflect the actual SQL column names (accounting for @Column(name = …) overrides). Use these inside findWhere, observeWhere, deleteWhere, and count lambdas.