Kotlin Room Database Basics

Room is Android's official database library. It provides an abstraction layer over SQLite, replacing raw SQL queries with Kotlin-annotated classes. Room verifies your queries at compile time, integrates seamlessly with coroutines, and works naturally with LiveData and Flow.

Room Architecture


Your Code
  │
  ▼
┌───────────────────────────────────────────────────┐
│                    Room                           │
│                                                   │
│  ┌─────────────┐   ┌──────────────┐               │
│  │   @Entity   │   │    @Dao      │               │
│  │ (data model │   │ (queries:    │               │
│  │  = table)   │   │  insert,     │               │
│  └─────────────┘   │  delete,     │               │
│                    │  update,     │               │
│  ┌─────────────┐   │  select)     │               │
│  │  @Database  │   └──────────────┘               │
│  │ (ties it all│                                  │
│  │  together)  │                                  │
│  └─────────────┘                                  │
│                                                   │
└───────────────────────────────────────────────────┘
  │
  ▼
SQLite (actual file on device)

Step 1: Add Dependencies

// In app/build.gradle.kts:
plugins {
    id("com.google.devtools.ksp") version "1.9.0-1.0.13"  // or kapt
}

dependencies {
    implementation("androidx.room:room-runtime:2.6.1")
    implementation("androidx.room:room-ktx:2.6.1")     // coroutines support
    ksp("androidx.room:room-compiler:2.6.1")
}

Step 2: Define the Entity (Table)

import androidx.room.Entity
import androidx.room.PrimaryKey

@Entity(tableName = "notes")
data class Note(
    @PrimaryKey(autoGenerate = true)
    val id: Int = 0,

    val title: String,
    val content: String,
    val createdAt: Long = System.currentTimeMillis()
)

Step 3: Create the DAO (Data Access Object)

import androidx.room.*
import kotlinx.coroutines.flow.Flow

@Dao
interface NoteDao {

    @Insert(onConflict = OnConflictStrategy.REPLACE)
    suspend fun insert(note: Note)

    @Delete
    suspend fun delete(note: Note)

    @Update
    suspend fun update(note: Note)

    @Query("SELECT * FROM notes ORDER BY createdAt DESC")
    fun getAllNotes(): Flow>   // returns a live stream of notes

    @Query("SELECT * FROM notes WHERE id = :noteId")
    suspend fun getNoteById(noteId: Int): Note?

    @Query("DELETE FROM notes WHERE id = :noteId")
    suspend fun deleteById(noteId: Int)

    @Query("SELECT COUNT(*) FROM notes")
    suspend fun count(): Int
}

Step 4: Define the Database

import android.content.Context
import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase

@Database(entities = [Note::class], version = 1, exportSchema = false)
abstract class NoteDatabase : RoomDatabase() {

    abstract fun noteDao(): NoteDao

    companion object {
        @Volatile
        private var INSTANCE: NoteDatabase? = null

        fun getInstance(context: Context): NoteDatabase {
            return INSTANCE ?: synchronized(this) {
                Room.databaseBuilder(
                    context.applicationContext,
                    NoteDatabase::class.java,
                    "note_database"
                ).build().also { INSTANCE = it }
            }
        }
    }
}

Step 5: Use Room in a ViewModel

import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.launch

class NoteViewModel(private val dao: NoteDao) : ViewModel() {

    val notes: StateFlow> = dao.getAllNotes()
        .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), emptyList())

    fun addNote(title: String, content: String) = viewModelScope.launch {
        dao.insert(Note(title = title, content = content))
    }

    fun deleteNote(note: Note) = viewModelScope.launch {
        dao.delete(note)
    }

    fun updateNote(note: Note) = viewModelScope.launch {
        dao.update(note)
    }
}

Step 6: Connect in Activity

class MainActivity : AppCompatActivity() {

    private lateinit var viewModel: NoteViewModel

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val database = NoteDatabase.getInstance(this)
        val factory  = NoteViewModelFactory(database.noteDao())
        viewModel    = ViewModelProvider(this, factory)[NoteViewModel::class.java]

        // Collect notes and update UI
        lifecycleScope.launch {
            viewModel.notes.collect { notes ->
                // Update RecyclerView adapter with latest notes
            }
        }

        // Add a note
        viewModel.addNote("Shopping", "Milk, Bread, Eggs")
    }
}

Room Query Examples

@Dao
interface ProductDao {
    @Query("SELECT * FROM products WHERE price BETWEEN :min AND :max ORDER BY price ASC")
    suspend fun getByPriceRange(min: Double, max: Double): List

    @Query("SELECT * FROM products WHERE category = :cat AND inStock = 1")
    fun getLiveByCategory(cat: String): Flow>

    @Query("UPDATE products SET stock = stock - 1 WHERE id = :id")
    suspend fun decrementStock(id: Int)

    @Query("SELECT SUM(price * qty) FROM cart_items WHERE userId = :uid")
    suspend fun getCartTotal(uid: Int): Double?
}

Leave a Comment

Your email address will not be published. Required fields are marked *