Skip to content

CRUD Operations

All write and read methods are suspend. Call them from a CoroutineScope.

Insert

Pass id = 0 for auto-generated primary keys — SQLite assigns the real id.

lifecycleScope.launch {
    // Insert a project
    projectRepo.insert(
        Project(name = "Website Redesign", color = "#1976D2")
    )

    // Insert a task under project id 1
    taskRepo.insert(
        Task(projectId = 1L, title = "Write copy", priority = 2)
    )

    // Insert checklist items for task id 1
    checklistRepo.insert(ChecklistItem(taskId = 1L, text = "Hero section"))
    checklistRepo.insert(ChecklistItem(taskId = 1L, text = "Features section"))
    checklistRepo.insert(ChecklistItem(taskId = 1L, text = "Pricing table"))
}

Read

lifecycleScope.launch {
    val project: Project?      = projectRepo.findById(1L)   // null if not found
    val allTasks: List<Task>   = taskRepo.findAll()
    val item: ChecklistItem?   = checklistRepo.findById(3L)
}

Update

Use data class copy() to describe only what changed. All other fields keep their current values.

lifecycleScope.launch {
    val task = taskRepo.findById(1L) ?: return@launch

    // Change status and priority, leave everything else unchanged
    taskRepo.update(task.copy(status = "IN_PROGRESS", priority = 3))
}

Delete

lifecycleScope.launch {
    checklistRepo.delete(itemId)   // delete by primary key
    taskRepo.delete(task.id)
}

Bulk insert

lifecycleScope.launch {
    taskRepo.insertAll(listOf(
        Task(projectId = 1L, title = "Design mockups"),
        Task(projectId = 1L, title = "Write copy"),
        Task(projectId = 1L, title = "Build prototype")
    ))
}

insertAll wraps all inserts in a single transaction — reactive observers receive one emission after all rows are committed, not one per row.

No return value from insert

insert does not return the generated id. If you need the id immediately after insert, use findAll().last().id or query by a unique field. This limitation will be addressed in a future release.