Room: Key Concepts, Entity, DAO, and Working with Databases

Author: IT Sectr Published: 2026-05-04 Reading time: 8 min

Room is a library for working with SQLite in Android, part of Jetpack. It provides an abstraction layer over raw SQLite, automating table creation, query execution, and data conversion into Kotlin and Java objects. According to Android Developers, Room compiles SQL queries at build time, checking syntax correctness and relationships between Entity and tables.

Key Takeaways

  • Room is a Jetpack ORM library for working with SQLite in Android applications.
  • Entity is a class annotated with @Entity, each instance corresponds to a row in a table.
  • DAO is a Data Access Object with annotated methods for SQL queries.
  • Database is an abstract class extending RoomDatabase, linking Entity and DAO.
  • Migration is a mechanism for safely changing the database schema without losing user data.

What is Room and Why Do You Need It

Room is a persistence library from Android Jetpack that provides object-relational mapping for SQLite. Room solves three main problems of raw SQLite: writing large amounts of boilerplate code for creating tables, lack of SQL query verification at compile time, and manual conversion of Cursor to objects.

The library uses an annotation processor (kapt or KSP) that generates the implementation of abstract RoomDatabase and DAO classes at build time. This ensures that syntax errors in SQL and type mismatches are discovered before running the application, rather than at runtime after publishing to Google Play.

According to Google I/O 2023, Room is used in 68% of Android applications that work with local data. It is the standard for on-device data storage, recommended by Google for all new projects — instead of the outdated SQLiteOpenHelper and ContentProvider.

Integrate Room into projects that require local data caching from a server, offline mode, or storing structured user data with the ability to run complex SQL queries.

Room is part of Android Jetpack and is officially recommended by Google for all new projects working with local data. Unlike Realm or ObjectBox, Room uses native SQLite, ensuring compatibility with any third-party database tools — from DB Browser to DataGrip. Developers can open the .db file of the application and execute SQL queries directly, simplifying debugging and data analysis during development.

Entity and Annotations in Room

Entity is a data class annotated with @Entity that Room transforms into a database table. Each field of the class becomes a table column, and each instance becomes a row. Room uses reflection to access fields, so the @PrimaryKey annotation is required for a mandatory identifier.

Main Annotations

The @Entity annotation tells Room that the class is a table. The tableName parameter sets the table name if it differs from the class name. @PrimaryKey defines the primary key with auto-generation support via autoGenerate = true.

kotlin
@Entity(tableName = "users")
data class User(
    @PrimaryKey(autoGenerate = true)
    val id: Int = 0,
    @ColumnInfo(name = "full_name")
    val name: String,
    @Ignore
    val tempData: String?
)

@ColumnInfo specifies the column name in the table if it differs from the Kotlin field name. @Ignore excludes a field from the table — it will not be saved to the database. @ForeignKey describes foreign keys for relationships between tables with cascade operations on delete or update.

Room supports nested objects through the @Embedded annotation. The fields of the nested class are expanded into columns of the parent table with a prefix to avoid name conflicts. For example, an Address class with city and street fields embedded in User will create address_city and address_street columns in the users table, eliminating the need for separate tables for simple value objects.

Type Converters

Room only supports primitive types and their wrappers. To store lists, Date, or custom types, use @TypeConverter — static methods for converting between a custom type and a SQLite primitive, for example, between List and a JSON string.

DAO and SQL Queries

DAO (Data Access Object) is an interface or abstract class annotated with @Dao that contains methods for data access. Each method is annotated with an SQL operation: @Insert, @Update, @Delete, or @Query with an explicit SQL query.

@Query with Compile-Time Verification

The @Query annotation takes an SQL string that Room verifies at compile time for syntax correctness and column name matching with Entity fields. Room supports parameterized queries using the :paramName syntax.

kotlin
@Dao
interface UserDao {
    @Query("SELECT * FROM users WHERE id = :userId")
    suspend fun getUserById(userId: Int): User?

    @Insert(onConflict = OnConflictStrategy.REPLACE)
    suspend fun insertUser(user: User)

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

@Insert supports OnConflictStrategy options for handling conflicts when inserting duplicate records. Flow as a return type provides reactive UI updates on every data change in the table — the subscription automatically restarts on any INSERT, UPDATE, or DELETE.

@Transaction for Complex Operations

The @Transaction annotation guarantees atomic execution of multiple operations within a single transaction block. Room locks the database during execution, preventing race conditions during concurrent access from multiple threads.

Database and Schema Migrations

RoomDatabase is an abstract class that combines Entity and DAO into a single database access point. It is created via Room.databaseBuilder with the schema version and a list of Entity classes. The database instance should be created as a singleton using a lazy delegate to avoid multiple connections.

Migrations

A Migration in Room is a Migration class that describes an SQL script for transitioning from an old schema version to a new one. If a migration is not provided when the schema changes, Room throws an IllegalStateException. This protects against accidental user data loss when updating the application.

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

val db = Room.databaseBuilder(
    getApplication(),
    AppDatabase::class.java,
    "app_database"
).addMigrations(migration_1_2)
 .build()

For development, you can use fallbackToDestructiveMigration, which deletes the old database and creates a new one on version mismatch. This mode is intended only for debugging — production releases must include proper migrations.

For database testing, Room provides a special class Room.inMemoryTestBuilder that creates an in-memory database without saving to disk. After each test completes, the database is automatically destroyed, ensuring complete isolation of test scenarios. Combined with the android-arch-core-testing library, developers can manage the database lifecycle and verify migration correctness without manually cleaning state.

Room performance directly depends on query structure and indexes. For analyzing slow queries, Room provides the enableQueryCallback flag, which logs all SQL queries with execution time. Developers can use this log to find queries running longer than 100 milliseconds and optimize them by adding composite indexes via the @Index annotation in @Entity or rewriting subqueries as direct JOIN operations using @Relation.

Room also supports database encryption via SQLCipher. Adding the net.zetetic:android-database-sqlcipher library and using SupportFactory instead of the standard one provides transparent encryption of all data on disk without changing DAO queries or Entity structure. This is necessary for applications handling personal user data and complies with GDPR and Russian Federal Law 152-FZ on personal data protection. The encryption password can be stored in Android Keystore to protect against extraction through tooling on rooted devices.

Room with Kotlin Coroutines and Flow

Room natively supports Kotlin Coroutines starting from version 2.1. DAO methods can be suspend functions that execute queries in the background thread without blocking the main thread. Room automatically manages dispatchers, using Dispatchers.IO for read and write queries.

For reactive queries, Room returns a Flow — a cold data stream that emits a new value on every change to the affected table. ViewModel subscribes to Flow via stateIn or collect, providing automatic UI updates without manually notifying the adapter.

Room also supports Paging 3 through a special PagingSource implementation that loads data page by page from SQLite. This is efficient for large lists with thousands of records: Paging 3 loads only the rows visible on screen and automatically updates them on database changes.

Use Paging 3 with Room for displaying news feeds, operation logs, or product lists with offline access and infinite scrolling.

Frequently Asked Questions

How is Room different from SQLiteOpenHelper?

Room automates table creation, Cursor-to-object conversion, and SQL verification at compile time. SQLiteOpenHelper requires writing the schema manually, handling Cursor, and has no query verification before running the application, which increases the risk of errors.

Do I need to write migrations for every schema change?

Yes, when changing an Entity (adding/removing a field, changing a type), a migration is required. Without it, Room throws an IllegalStateException on startup. For development, you can enable fallbackToDestructiveMigration, but production releases require correct migration scripts.

Does Room support relationships between tables?

Room supports @ForeignKey for cascade operations and @Relation for nested objects. For complex JOIN queries, use the @Transaction annotation with @Query returning a POJO with nested entities via @Embedded and @Relation.

Can I use Room with Java without Kotlin?

Yes, Room is fully compatible with Java. Instead of suspend functions, use LiveData or RxJava Observable; instead of Flow, use LiveData. Room with Java supports all the same annotations but requires more boilerplate code for asynchronous operations.

How does Room database encryption work?

Room supports encryption via SQLCipher by Zetetic. Instead of Room.databaseBuilder, use SupportFactory from the net.zetetic:android-database-sqlcipher library, passing the encryption password. All data on disk will be encrypted transparently to DAO queries.

Summary

  • Room is a Jetpack ORM library for SQLite with compile-time SQL verification.
  • @Entity describes the table, @PrimaryKey is the identifier, @ColumnInfo is the column name.
  • @Dao contains methods with @Query, @Insert, @Update, and @Delete for data access.
  • RoomDatabase combines Entity and DAO, created via Room.databaseBuilder.
  • Migrations (Migration) describe SQL scripts for schema changes without data loss.
  • Room natively supports Kotlin Coroutines (suspend) and Flow for reactive UI updates.
  • For large lists, use Paging 3 with Room via PagingSource for paginated loading.

We will develop a mobile application turnkey

IT Sectr creates iOS and Android applications for startups and businesses since 2017. We will advise you and propose the best solution.

Discuss the project

Read also