Room with Coroutines

Entity, DAO, Database

Room is the SQLite library of Android Jetpack. DAO functions can be suspend (one-shot) or return Flow (the query re-emits whenever the table changes). Room runs them on its own background threads, so they are main-safe.

@Entity(tableName = "cart_items")
data class CartItemEntity(
    @PrimaryKey val productId: Long,
    val name: String,
    val price: Int,
    val quantity: Int,
)

@Dao
interface CartDao {
    @Query("SELECT * FROM cart_items ORDER BY name")
    fun observeCart(): Flow<List<CartItemEntity>>            // live updates

    @Query("SELECT * FROM cart_items WHERE productId = :id")
    suspend fun find(id: Long): CartItemEntity?

    @Upsert
    suspend fun upsert(item: CartItemEntity)

    @Query("DELETE FROM cart_items")
    suspend fun clear()
}

@Database(entities = [CartItemEntity::class], version = 1, exportSchema = true)
abstract class ShopDatabase : RoomDatabase() {
    abstract fun cartDao(): CartDao

    companion object {
        fun build(context: Context): ShopDatabase =
            Room.databaseBuilder(context, ShopDatabase::class.java, "shop.db").build()
    }
}

Transactions and Flow

class CartRepository(private val db: ShopDatabase) {
    private val dao = db.cartDao()

    val cartTotal: Flow<Int> = dao.observeCart()
        .map { items -> items.sumOf { it.price * it.quantity } }

    suspend fun add(product: Product) = db.withTransaction {       // room-ktx
        val existing = dao.find(product.id)
        dao.upsert(
            existing?.copy(quantity = existing.quantity + 1)
                ?: CartItemEntity(product.id, product.name, product.price, 1),
        )
    }
}

Note: Room processes annotations with KSP (ksp("androidx.room:room-compiler:...")), which is much faster than the old kapt.