Skip to content

Intermediate: Android basics · Saving data and calling APIs · topic 31 of 50 · 35 min

Room database

Explanation

Room is Android's recommended library for a local SQLite database. You describe a table with an @Entity data class, write queries in a @Dao interface with @Insert, @Update, @Delete and @Query, and create the database from a @Database class. DAO functions can be suspend for one-off work or return Flow to get live updates whenever the table changes.

Why a local database?

Many apps must remember data between launches and work without internet: an expense tracker, a notes app, a cached product catalogue. SQLite is a small database engine built into every Android phone. Writing raw SQLite code is long and error-prone, so Google provides Room, a library on top of SQLite that checks your SQL queries at compile time and maps rows to Kotlin objects.

PieceWhat it is
@EntityA data class that becomes a table; each property is a column
@DaoAn interface with functions for reading and writing; Room generates the code
@DatabaseAn abstract class listing the entities and giving access to the DAOs

Entity

kotlin
@Entity(tableName = "expenses")
data class Expense(
    @PrimaryKey(autoGenerate = true) val id: Long = 0,
    val title: String,
    val amountPaise: Long,
    val category: String,
    val createdAt: Long = System.currentTimeMillis(),
)
A primary key identifies each row uniquely; autoGenerate lets the database assign it.

DAO

kotlin
@Dao
interface ExpenseDao {
    @Query("SELECT * FROM expenses ORDER BY createdAt DESC")
    fun observeAll(): Flow<List<Expense>>                 // live updates

    @Query("SELECT SUM(amountPaise) FROM expenses WHERE category = :category")
    fun totalFor(category: String): Flow<Long?>

    @Insert
    suspend fun insert(expense: Expense)

    @Update
    suspend fun update(expense: Expense)

    @Delete
    suspend fun delete(expense: Expense)
}
:category binds the function parameter safely into the query (no SQL injection).

Two styles of functions: suspend functions do one piece of work (insert, update, a single read) and Room runs them off the main thread. Functions returning Flow emit the current result and then a new result every time the table changes, which is perfect for a list screen that should update itself after an insert.

Database

kotlin
@Database(entities = [Expense::class], version = 1)
abstract class AppDatabase : RoomDatabase() {
    abstract fun expenseDao(): ExpenseDao
}

// Create ONE instance for the whole app (for example in a Hilt module or an app container):
val db = Room.databaseBuilder(applicationContext, AppDatabase::class.java, "app.db").build()
Creating a database is expensive; share a single instance.

Room generates code at build time using KSP (Kotlin Symbol Processing). Add the Room runtime, room-ktx and the room-compiler with ksp(...) in your Gradle file; the official Room guide shows the current setup. Older tutorials use kapt, which is in maintenance mode; prefer KSP.

Migrations

When you change an entity (add a column), you must increase the database version and tell Room how to move existing users' data: a Migration with the SQL to run, or an AutoMigration for simple changes. fallbackToDestructiveMigration() simply deletes and recreates the database, which loses user data; never ship it for important data without thinking.

Think of it like this: Room is like a well-organised office register. The entity is the printed column layout, the DAO is the clerk who knows exactly how to add, change, find and remove entries, and the database is the register itself. A migration is reprinting the register with a new column while copying every old entry across.

Common mistakes

  • Running database calls on the main thread: Room refuses (IllegalStateException). Use suspend functions or Flow.
  • Creating a new database instance on every screen. Use a single shared instance.
  • Changing an entity without increasing the version and adding a migration: the app crashes on update for existing users.
  • Storing money as Double. Store paise as Long.

Need help?

Stuck on a word or an idea? Search this course, or every course, and jump straight to the explanation or a worked question.

Try:

Watch the lesson

Room database · 1/6

Room = SQLite made easy

Room is Android's recommended library for a local SQLite database. It checks your queries at compile time.

Prefer a video by a teacher? Find free videos on “android room database tutorial entity dao flow kotlin” on YouTube ↗

In real life: A personal expense tracker

An expense tracker stores every expense in Room. The home screen shows a live list from observeAll() and the monthly food total from totalFor("Food"). When the user adds an expense, the ViewModel calls insert; both the list and the total update by themselves, because they are Flows backed by the table. Everything works in airplane mode.

kotlin
class ExpenseViewModel(private val dao: ExpenseDao) : ViewModel() {
    val expenses: StateFlow<List<Expense>> = dao.observeAll()
        .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), emptyList())

    fun add(title: String, rupees: Long, category: String) {
        viewModelScope.launch { dao.insert(Expense(title = title, amountPaise = rupees * 100, category = category)) }
    }
}
stateIn turns the database Flow into a StateFlow for the UI.

Practice (0/3 done)

Task 1 · Question

Which DAO return type gives the UI automatic updates when rows are inserted?

Task 2 · Question

You added a column to an entity and shipped an update without a migration. What happens for existing users?

Task 3 · Practice

Build the data layer for a reading list: entity Book(id, title, author, pages, finished), a DAO with observeUnfinished(): Flow<List<Book>>, insert, markFinished(id) as an @Query UPDATE, and countFinished(): Flow<Int>. Create the database and show the unfinished books in a LazyColumn.

Quick quiz

Question 1

1.What database engine does Room use underneath?

Question 2

2.Which annotation marks the interface with query functions?

Question 3

3.Why is :category used in @Query instead of joining strings?

Question 4

4.Which annotation processor setup does the current Room guide recommend?

Read more (free)

Open a question to see the answer and why. The first ones are this topic's own quiz, so try the quiz above before you peek.

What database engine does Room use underneath?

Answer: SQLite

Room is a layer over SQLite.

Which annotation marks the interface with query functions?

Answer: @Dao

DAO means Data Access Object.

Why is :category used in @Query instead of joining strings?

Answer: Room binds the value safely, preventing SQL injection

Bound parameters are safe and checked.

Which annotation processor setup does the current Room guide recommend?

Answer: KSP

KSP is faster; kapt is in maintenance mode.

What can Room return to get updates whenever a table changes?Related

Answer: A Flow<List<T>>

Room emits a new list on every change.

From Flow and StateFlow

Why should the Migrations folder be committed to Git?Related

Answer: It is the reviewed history of schema changes that every environment applies

Migrations are code that evolves the schema consistently.

From Entity Framework Core: databases with C#

What is a database migration?Related

Answer: A numbered file that changes the schema by one step, applied in order

Migrations are small, ordered schema changes kept in the repository, so every database can be built the same way.

From Creating the database: schema, migrations and seed data

What does RETURNING id do at the end of an INSERT?Related

Answer: Gives back the id of the new row

RETURNING sends back values from the changed rows, such as the new id.

From INSERT, UPDATE and DELETE

Pass the quiz (60% or more) or mark this topic as done when you are ready.

Your progress is saved on this device only.

Next steps

  1. 3 more topics in “Saving data and calling APIs” before its exam: Networking with Retrofit, JSON models and handling errors, Offline-first apps and DataStore.
  2. Module 8 exam: Saving data and calling APIs · 13 questions · 25 min, without help. 4 topics of this module still to finish first.
  3. Then the next module, Permissions, Material 3 and polish. 50 topics left in the course before the final exam.

← Previous: Dependency injection with Hilt