<!-- mobian-agent-page publisher="dailydev" canonical="https://daily.dev/blog/android-room-persistence-library-complete-guide" -->

---
title: Android Room Persistence Library: Complete Guide | daily.dev
description: Explore the Android Room Persistence Library for efficient database management, including setup, entities, DAOs, and integration with Kotlin Coroutines.
canonical: https://daily.dev/blog/android-room-persistence-library-complete-guide/
og:type: article
og:url: https://daily.dev/blog/android-room-persistence-library-complete-guide/
og:title: Android Room Persistence Library: Complete Guide | daily.dev
og:description: Explore the Android Room Persistence Library for efficient database management, including setup, entities, DAOs, and integration with Kotlin Coroutines.
og:image: https://media.daily.dev/image/upload/s--uMVAzRS4--/f_auto,q_auto/v1/recruiter-landing/66ca779f7f8fa924d699dce8_8d31fff9eb6ebffa8cdb1f6ee510fdde_ddddcdd49a?_a=BAMAMiB80
og:site_name: daily.dev
og:locale: en_US
article:published_time: 2024-08-25
article:modified_time: 2026-05-25T06:17:06.182Z
article:author: Alex Carter
twitter:card: summary_large_image
twitter:site: @dailydotdev
twitter:creator: @dailydotdev
twitter:title: Android Room Persistence Library: Complete Guide | daily.dev
twitter:description: Explore the Android Room Persistence Library for efficient database management, including setup, entities, DAOs, and integration with Kotlin Coroutines.
twitter:image: https://media.daily.dev/image/upload/s--uMVAzRS4--/f_auto,q_auto/v1/recruiter-landing/66ca779f7f8fa924d699dce8_8d31fff9eb6ebffa8cdb1f6ee510fdde_ddddcdd49a?_a=BAMAMiB80
---

Room simplifies database operations in Android. It offers:

-   Compile-time query checks
-   Less boilerplate code
-   Automatic object mapping
-   Built-in migration support

Key parts:

1.  Entities (database tables)
2.  DAOs (database operations)
3.  Database class

Room works well with:

-   [Kotlin Coroutines](https://kotlinlang.org/docs/coroutines-overview.html)
-   [LiveData](https://developer.android.com/topic/libraries/architecture/livedata)
-   [ViewModel](https://developer.android.com/topic/libraries/architecture/viewmodel)
-   [Paging library](https://developer.android.com/topic/libraries/architecture/paging/v3-overview)

| Feature | Room | [SQLite](https://www.sqlite.org/) |
| --- | --- | --- |
| Query validation | Compile-time | Runtime |
| Object mapping | Automatic | Manual |
| Boilerplate code | Minimal | Extensive |
| Migration support | Built-in | Manual |

This guide covers setup, entities, DAOs, database management, testing, and optimization for Room in Android.

## Related video from YouTube

::: @iframe https://www.youtube-nocookie.com/embed/bOd3wO0uFr8

## Room Architecture Basics

Room provides an abstract layer over SQLite with three main components:

1.  **Entity**: Represents a database table
    
    ```kotlin
    @Entity
    data class User(
        @PrimaryKey val uid: Int,
        @ColumnInfo(name = "first_name") val firstName: String?,
        @ColumnInfo(name = "last_name") val lastName: String?
    )
    ```
    
2.  **DAO**: Defines database operations
    
    ```kotlin
    @Dao
    interface UserDao {
        @Query("SELECT * FROM user")
        fun getAll(): List<User>
    
        @Insert
        fun insertAll(vararg users: User)
    
        @Delete
        fun delete(user: User)
    }
    ```
    
3.  **Database**: Main access point for app's data
    
    ```kotlin
    @Database(entities = [User::class], version = 1)
    abstract class AppDatabase : RoomDatabase() {
        abstract fun userDao(): UserDao
    }
    ```
    

Room simplifies database operations:

1.  App interacts with Database instance
2.  Database uses DAOs for operations
3.  DAOs interact with Entities (database tables)

This structure allows for more organized code compared to raw SQLite queries.

## Adding Room to Your Project

To add Room to your Android project:

1.  Include dependencies in `build.gradle`:

```gradle
dependencies {
    def room_version = "2.5.2"
    implementation "androidx.room:room-runtime:$room_version"
    implementation "androidx.room:room-ktx:$room_version"
    kapt "androidx.room:room-compiler:$room_version"
    implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.2"
}
```

2.  Apply plugins in project-level `build.gradle`:

```gradle
plugins {
    id 'com.android.application'
    id 'kotlin-android'
    id 'kotlin-kapt'
}
```

3.  Sync project with Gradle files.
    
4.  For latest Android and [Jetpack Compose](https://app.daily.dev/tags/jetpack-compose), use [Kotlin](https://kotlinlang.org/) Symbol Processing (KSP):
    

```gradle
plugins {
    id 'com.google.devtools.ksp' version '1.8.21-1.0.11'
}

dependencies {
    def room_version = "2.5.2"
    implementation "androidx.room:room-runtime:$room_version"
    implementation "androidx.room:room-ktx:$room_version"
    ksp "androidx.room:room-compiler:$room_version"
}
```

5.  Sync project again after changes.

## Creating Entities

Entities represent database tables in Room. Here's how to create them:

```kotlin
@Entity(tableName = "users")
data class User(
    @PrimaryKey val id: Int,
    val name: String,
    @ColumnInfo(name = "email_address") val email: String
)
```

Key annotations:

| Annotation | Purpose | Example |
| --- | --- | --- |
| @Entity | Marks class as entity | @Entity(tableName = "books") |
| @PrimaryKey | Defines primary key | @PrimaryKey val id: Int |
| @ColumnInfo | Specifies column details | @ColumnInfo(name = "author\_name") val author: String |
| @Ignore | Excludes field from table | @Ignore val temporaryFlag: Boolean |

For entity relationships:

1.  Define parent entity:

```kotlin
@Entity(tableName = "schools")
data class School(
    @PrimaryKey val id: Int,
    val name: String
)
```

2.  Define child entity with foreign key:

```kotlin
@Entity(
    tableName = "students",
    foreignKeys = [ForeignKey(
        entity = School::class,
        parentColumns = ["id"],
        childColumns = ["school_id"],
        onDelete = ForeignKey.CASCADE
    )]
)
data class Student(
    @PrimaryKey val id: Int,
    val name: String,
    @ColumnInfo(name = "school_id") val schoolId: Int
)
```

3.  Create relationship class:

```kotlin
data class SchoolWithStudents(
    @Embedded val school: School,
    @Relation(
        parentColumn = "id",
        entityColumn = "school_id"
    )
    val students: List<Student>
)
```

This setup allows querying a school and its students together.

## Using Data Access Objects (DAOs)

DAOs define database operations in Room:

```kotlin
@Dao
interface UserDao {
    @Insert
    suspend fun insertUser(user: User)

    @Query("SELECT * FROM users")
    fun getAllUsers(): List<User>

    @Update
    suspend fun updateUser(user: User)

    @Delete
    suspend fun deleteUser(user: User)
}
```

Basic operations:

| Operation | Annotation | Description |
| --- | --- | --- |
| Create | @Insert | Adds data |
| Read | @Query | Retrieves data |
| Update | @Update | Modifies data |
| Delete | @Delete | Removes data |

For custom queries:

```kotlin
@Query("SELECT * FROM users WHERE age BETWEEN :minAge AND :maxAge")
fun getUsersBetweenAges(minAge: Int, maxAge: Int): List<User>
```

Use with Flow for [reactive programming](https://app.daily.dev/tags/reactive-programming):

```kotlin
@Query("SELECT * FROM users ORDER BY name ASC")
fun getAllUsersAlphabetically(): Flow<List<User>>
```

## Setting Up and Managing Databases

Create your RoomDatabase class:

```kotlin
@Database(entities = [User::class], version = 1)
abstract class AppDatabase : RoomDatabase() {
    abstract fun userDao(): UserDao

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

        fun getDatabase(context: Context): AppDatabase {
            return INSTANCE ?: synchronized(this) {
                val instance = Room.databaseBuilder(
                    context.applicationContext,
                    AppDatabase::class.java,
                    "app_database"
                ).build()
                INSTANCE = instance
                instance
            }
        }
    }
}
```

For database updates:

1.  Increase version number
2.  Create Migration class:

```kotlin
val MIGRATION_1_2 = object : Migration(1, 2) {
    override fun migrate(database: SupportSQLiteDatabase) {
        database.execSQL("ALTER TABLE User ADD COLUMN age INTEGER NOT NULL DEFAULT 0")
    }
}
```

3.  Add migration to database builder:

```kotlin
Room.databaseBuilder(context, AppDatabase::class.java, "app_database")
    .addMigrations(MIGRATION_1_2)
    .build()
```

For non-critical data, use:

```kotlin
Room.databaseBuilder(context, AppDatabase::class.java, "app_database")
    .fallbackToDestructiveMigration()
    .build()
```

## Advanced Room Features

Room offers advanced features for better performance:

1.  Type Converters:

```kotlin
class Converters {
    @TypeConverter
    fun fromBitmap(bitmap: Bitmap?): ByteArray? {
        // Convert Bitmap to ByteArray
    }

    @TypeConverter
    fun toBitmap(byteArray: ByteArray?): Bitmap? {
        // Convert ByteArray to Bitmap
    }
}

@Database(entities = [Run::class], version = 1)
@TypeConverters(Converters::class)
abstract class RunningDatabase : RoomDatabase() {
    // Database implementation
}
```

2.  Full-text search:

```kotlin
@Fts4
@Entity(tableName = "notes")
data class Note(
    @PrimaryKey @ColumnInfo(name = "rowid") val id: Int,
    @ColumnInfo(name = "title") val title: String,
    @ColumnInfo(name = "content") val content: String
)

@Dao
interface NoteDao {
    @Query("SELECT * FROM notes WHERE notes MATCH :query")
    fun searchNotes(query: String): List<Note>
}
```

3.  Transactions:

```kotlin
@Dao
abstract class InvoiceStore {
    @Insert
    abstract fun insertInvoice(invoice: Invoice)

    @Insert
    abstract fun insertItems(items: List<InvoiceItem>)

    @Transaction
    open fun insertInvoiceWithItems(invoice: Invoice, items: List<InvoiceItem>) {
        insertInvoice(invoice)
        insertItems(items)
    }
}
```

4.  Background queries:

```kotlin
@Dao
interface UserDao {
    @Query("SELECT * FROM users")
    suspend fun getAllUsers(): List<User>
}

// In ViewModel or Repository
viewModelScope.launch {
    val users = userDao.getAllUsers()
    // Update UI with users
}
```

## Room with [Kotlin Coroutines](https://kotlinlang.org/docs/coroutines-overview.html)

![Kotlin Coroutines](https://assets.seobotai.com/daily.dev/66ca779f7f8fa924d699dce8/d1df3606cac15b1f7813e30d993eabb4.jpg)

Room works well with Kotlin Coroutines for efficient database operations:

1.  Add dependency:

```kotlin
dependencies {
    implementation "androidx.room:room-ktx:2.5.0"
}
```

2.  Create suspend functions in DAOs:

```kotlin
@Dao
interface UserDao {
    @Query("SELECT * FROM user")
    suspend fun getAll(): List<User>

    @Insert
    suspend fun insertAll(users: List<User>)

    @Delete
    suspend fun delete(user: User)
}
```

3.  Use in ViewModel:

```kotlin
class UserViewModel(private val userDao: UserDao) : ViewModel() {
    fun loadUsers() {
        viewModelScope.launch {
            val users = userDao.getAll()
            // Update UI with users
        }
    }
}
```

4.  Use Flow for observable queries:

```kotlin
@Dao
interface UserDao {
    @Query("SELECT * FROM user ORDER BY name ASC")
    fun getAllUsersFlow(): Flow<List<User>>
}
```

###### sbb-itb-bfaad5b

## Room with [LiveData](https://developer.android.com/topic/libraries/architecture/livedata) and Flow

![LiveData](https://assets.seobotai.com/daily.dev/66ca779f7f8fa924d699dce8/096a6711a7148767c151e8c6b2dd74d0.jpg)

Room integrates with LiveData and Flow for real-time data updates:

1.  LiveData in Room queries:

```kotlin
@Dao
interface UserDao {
    @Query("SELECT * FROM users ORDER BY name ASC")
    fun getAllUsers(): LiveData<List<User>>
}

class UserViewModel(private val userDao: UserDao) : ViewModel() {
    val allUsers: LiveData<List<User>> = userDao.getAllUsers()
}

// In UI
viewModel.allUsers.observe(this) { users ->
    // Update UI with users
}
```

2.  Flow for data updates:

```kotlin
@Dao
interface UserDao {
    @Query("SELECT * FROM users ORDER BY name ASC")
    fun getAllUsersFlow(): Flow<List<User>>
}

class UserViewModel(private val userDao: UserDao) : ViewModel() {
    val allUsers: StateFlow<List<User>> = userDao.getAllUsersFlow()
        .stateIn(viewModelScope, SharingStarted.Lazily, emptyList())
}

// In UI
lifecycleScope.launch {
    viewModel.allUsers.collect { users ->
        // Update UI with users
    }
}
```

| Feature | LiveData | Flow |
| --- | --- | --- |
| Lifecycle awareness | Yes | No (needs lifecycle-aware scope) |
| Backpressure handling | No  | Yes |
| Operator support | Limited | Extensive |
| UI thread safety | Yes | No (needs main dispatcher) |

## Testing Room Code

Test Room implementation with [JUnit](https://junit.org/) and [Mockito](https://site.mockito.org/):

```kotlin
@RunWith(AndroidJUnit4::class)
class UserDaoTest {
    private lateinit var database: AppDatabase
    private lateinit var userDao: UserDao

    @Before
    fun setup() {
        val context = ApplicationProvider.getApplicationContext<Context>()
        database = Room.inMemoryDatabaseBuilder(context, AppDatabase::class.java).build()
        userDao = database.userDao()
    }

    @After
    fun cleanup() {
        database.close()
    }

    @Test
    fun insertAndRetrieveUser() = runBlocking {
        val user = User(1, "John Doe")
        userDao.insert(user)
        val retrievedUser = userDao.getUserById(1)
        assertEquals(user, retrievedUser)
    }
}
```

For full database tests:

```kotlin
@RunWith(AndroidJUnit4::class)
class AppDatabaseTest {
    private lateinit var database: AppDatabase
    private lateinit var userDao: UserDao
    private lateinit var postDao: PostDao

    @Before
    fun setup() {
        val context = ApplicationProvider.getApplicationContext<Context>()
        database = Room.inMemoryDatabaseBuilder(context, AppDatabase::class.java).build()
        userDao = database.userDao()
        postDao = database.postDao()
    }

    @Test
    fun userWithPostsTest() = runBlocking {
        val user = User(1, "Jane Doe")
        userDao.insert(user)

        val post = Post(1, 1, "Test Post")
        postDao.insert(post)

        val userWithPosts = userDao.getUserWithPosts(1)
        assertEquals(1, userWithPosts.posts.size)
        assertEquals("Test Post", userWithPosts.posts[0].content)
    }
}
```

Use in-memory databases for faster testing:

```kotlin
Room.inMemoryDatabaseBuilder(context, AppDatabase::class.java)
    .allowMainThreadQueries() // For testing only
    .build()
```

## Making Room Faster

Optimize Room for better performance:

1.  Write better queries:
    -   Use specific column names
    -   Add LIMIT to queries
    -   Use WHERE clauses

```kotlin
@Query("SELECT id, name FROM users WHERE active = 1 LIMIT 100")
fun getActiveUsers(): List<UserMinimal>
```

2.  Use indexes:

```kotlin
@Entity(tableName = "users",
        indices = [Index(value = ["email"], unique = true)])
data class User(
    @PrimaryKey val id: Int,
    @ColumnInfo(name = "email") val email: String,
    @ColumnInfo(name = "name") val name: String
)
```

3.  Cache frequently accessed data:

```kotlin
class UserRepository(private val userDao: UserDao) {
    private var cachedUsers: List<User>? = null

    suspend fun getUsers(): List<User> {
        cachedUsers?.let { return it }
        return userDao.getAllUsers().also { cachedUsers = it }
    }

    suspend fun refreshUsers() {
        cachedUsers = userDao.getAllUsers()
    }
}
```

| Optimization Technique | Performance Impact |
| --- | --- |
| Better Queries | 20-50% faster |
| Using Indexes | Up to 10x faster |
| Data Caching | Near-instant access |

## Room with Other Android Tools

Room integrates well with other Android libraries:

1.  Room and ViewModel:

```kotlin
class UserViewModel(private val userDao: UserDao) : ViewModel() {
    val allUsers: LiveData<List<User>> = userDao.getAllUsers()
}

// In UI
viewModel.allUsers.observe(this) { users ->
    // Update UI with new user list
}
```

2.  Room with Paging:

```kotlin
@Dao
interface UserDao {
    @Query("SELECT * FROM users ORDER BY name ASC")
    fun getAllUsers(): PagingSource<Int, User>
}

class UserViewModel(private val userDao: UserDao) : ViewModel() {
    val userFlow = Pager(PagingConfig(pageSize = 20)) {
        userDao.getAllUsers()
    }.flow.cachedIn(viewModelScope)
}

// In UI
viewModel.userFlow.collectLatest { pagingData ->
    userAdapter.submitData(pagingData)
}
```

3.  Room and [WorkManager](https://developer.android.com/reference/androidx/work/WorkManager):

```kotlin
class DatabaseCleanupWorker(
    context: Context,
    params: WorkerParameters
) : CoroutineWorker(context, params) {

    override suspend fun doWork(): Result {
        val database = AppDatabase.getInstance(applicationContext)
        database.userDao().deleteOldUsers()
        return Result.success()
    }
}

// Schedule work
val cleanupWork = PeriodicWorkRequestBuilder<DatabaseCleanupWorker>(1, TimeUnit.DAYS)
    .build()

WorkManager.getInstance(context).enqueueUniquePeriodicWork(
    "database_cleanup",
    ExistingPeriodicWorkPolicy.KEEP,
    cleanupWork
)
```

## Room Tips and Common Mistakes

Good practices:

1.  Pre-populate database:

```kotlin
Room.databaseBuilder(context.applicationContext,  
        DataDatabase::class.java, "Sample.db")  
        .addCallback(object : Callback() {  
            override fun onCreate(db: SupportSQLiteDatabase) {  
                super.onCreate(db)  
                ioThread {  
                    getInstance(context).dataDao()  
                                        .insert(PREPOPULATE_DATA)  
                }  
            }  
        })
        .build()
```

2.  Use DAO inheritance:

```kotlin
interface BaseDao<T> {  
    @Insert  
    fun insert(vararg obj: T)  
}

@Dao  
abstract class DataDao : BaseDao<Data>() {  
    @Query("SELECT * FROM Data")  
    abstract fun getData(): List<Data>  
}
```

3.  Optimize queries:
    -   Use COUNT queries for counting
    -   Create separate GET queries for different use cases
    -   Use custom update queries for specific columns

Common mistakes:

1.  Ignoring database versioning
2.  Performing database operations on main thread
3.  Not using transactions for multiple operations
4.  Overusing hard deletes

Fixing Room problems:

1.  Migration issues: Provide migration paths or allow destructive migrations
2.  Performance problems: Use Database Inspector, create indexes, use @Relation annotation
3.  Relationship issues: Use @Embedded attribute or write custom queries

## Moving from [SQLite](https://www.sqlite.org/) to Room

![SQLite](https://assets.seobotai.com/daily.dev/66ca779f7f8fa924d699dce8/84aa2b5e4cbe0c6e0063332ceff14bb2.jpg)

Steps to switch:

1.  Update dependencies
2.  Create entity classes
3.  Define DAOs
4.  Create database class
5.  Update database operations
6.  Handle migrations

Keeping data safe:

1.  Back up existing data
2.  Test migrations thoroughly
3.  Implement fallback strategies
4.  Consider gradual rollout

## Conclusion

Room offers significant advantages over SQLite for Android developers:

-   Less code
-   Compile-time query validation
-   Built-in functions for large datasets
-   Structured approach with Entity-DAO-Repository model

Room integrates well with LiveData, ViewModel, and Coroutines, making it versatile for modern app development. While switching from SQLite requires careful planning, the long-term benefits in code maintainability and app performance make it worthwhile.

## FAQs

Room offers several benefits for Android developers:

1.  Simplified database management
2.  Compile-time query checks
3.  Less code through ORM features
4.  Improved performance

Compared to SQLite:

| Feature | Room | Direct SQLite |
| --- | --- | --- |
| Query validation | Compile-time | Runtime |
| Boilerplate code | Minimal | Extensive |
| Object mapping | Automatic | Manual |
| Migration support | Built-in | Manual |

Room's efficiency is evident in simple operations:

```kotlin
@Dao
interface UserDao {
    @Query("SELECT * FROM user")
    fun getAllUsers(): List<User>
}
```

This DAO method replaces multiple lines of SQLite code.

Tips for using Room:

-   Query only necessary fields
-   Use distinctUntilChanged() with Flow
-   Implement UPSERT operations with OnConflictStrategy.IGNORE

```json
{"@context":"https://schema.org","@graph":[{"@type":"Organization","@id":"https://daily.dev/#organization","name":"daily.dev","url":"https://daily.dev","logo":{"@type":"ImageObject","url":"https://daily.dev/og-image.png?v=a830cdf1","width":1200,"height":630},"sameAs":["https://twitter.com/dailydotdev","https://www.linkedin.com/company/dailydotdev","https://github.com/dailydotdev","https://www.instagram.com/dailydotdev"]},{"@type":"WebSite","@id":"https://daily.dev/#website","url":"https://daily.dev","name":"daily.dev","description":"Free, personalized developer news aggregator. Stay on top of software development news, AI coding tools, and web dev - curated daily from trusted sources.","publisher":{"@id":"https://daily.dev/#organization"},"potentialAction":{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https://daily.dev/search?q={search_term_string}"},"query-input":"required name=search_term_string"}},{"@type":"WebPage","@id":"https://daily.dev/blog/android-room-persistence-library-complete-guide/","url":"https://daily.dev/blog/android-room-persistence-library-complete-guide/","name":"Android Room Persistence Library: Complete Guide | daily.dev","description":"Explore the Android Room Persistence Library for efficient database management, including setup, entities, DAOs, and integration with Kotlin Coroutines.","inLanguage":"en-US","isPartOf":{"@id":"https://daily.dev/#website"},"timeRequired":"PT9M"},{"@type":"Article","@id":"https://daily.dev/blog/android-room-persistence-library-complete-guide/#article","headline":"Android Room Persistence Library: Complete Guide","url":"https://daily.dev/blog/android-room-persistence-library-complete-guide/","datePublished":"2024-08-25","dateModified":"2026-05-25T06:17:06.182Z","isPartOf":{"@id":"https://daily.dev/#website"},"publisher":{"@id":"https://daily.dev/#organization"},"mainEntityOfPage":{"@type":"WebPage","@id":"https://daily.dev/blog/android-room-persistence-library-complete-guide/"},"description":"Explore the Android Room Persistence Library for efficient database management, including setup, entities, DAOs, and integration with Kotlin Coroutines.","image":{"@type":"ImageObject","url":"https://media.daily.dev/image/upload/s--uMVAzRS4--/f_auto,q_auto/v1/recruiter-landing/66ca779f7f8fa924d699dce8_8d31fff9eb6ebffa8cdb1f6ee510fdde_ddddcdd49a?_a=BAMAMiB80"},"author":{"@type":"Person","name":"Alex Carter","url":"https://app.daily.dev/alexcarterdev"},"timeRequired":"PT9M","potentialAction":{"@type":"ReadAction","target":"https://daily.dev/blog/android-room-persistence-library-complete-guide/"}},{"@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https://daily.dev/"},{"@type":"ListItem","position":2,"name":"Blog","item":"https://daily.dev/blog/"},{"@type":"ListItem","position":3,"name":"Mobile","item":"https://daily.dev/categories/mobile/"},{"@type":"ListItem","position":4,"name":"Android Room Persistence Library: Complete Guide","item":"https://daily.dev/blog/android-room-persistence-library-complete-guide/"}]}]}
```

