Room Database Quiz
DATA › Storage
Which statement about returning a Flow from a Room @Query is correct?
- A Flow-returning @Query also has to be marked suspend
- Flow queries execute synchronously on the caller's thread
- Room emits a fresh value whenever the queried tables change
- Flow results work only when the query is wrapped in LiveData
Answer: Room emits a fresh value whenever the queried tables change
Observable Flow queries automatically emit a fresh result on every relevant table change, and they must not be marked suspend because the stream is already asynchronous.
You increase the @Database version from 1 to 2 but provide no Migration and no destructive fallback. What happens?
- Room throws an IllegalStateException when the DB is opened
- Room silently wipes the database and then keeps running as usual
- The project fails to compile because the version number is invalid
- Room auto-generates a migration from the exported schema file
Answer: Room throws an IllegalStateException when the DB is opened
Without a migration path or a destructive fallback, Room cannot reconcile the old and new schemas and throws an IllegalStateException at runtime.
You want to persist a Date field in a Room entity. What is the correct approach?
- Room stores Date directly, so you can save it without any converter
- Annotate the field with @Embedded to make Room persist the Date
- Use @Relation to map the Date value into a database column
- Use @TypeConverter methods to convert Date to and from Long
Answer: Use @TypeConverter methods to convert Date to and from Long
SQLite only stores primitives, String, and byte[], so a Date needs a type converter that maps it to a storable type such as Long.
What is the correct way to fetch a User together with their list of Pets in a single Room result?
- Add a List<Pet> field directly inside the @Entity User class
- Create a POJO with @Embedded User and @Relation List<Pet>
- Write a JOIN and Room will auto-nest the child rows into the parent
- Serialize the pets as a JSON string with a type converter instead
Answer: Create a POJO with @Embedded User and @Relation List<Pet>
Room maps relationships via a POJO that embeds the parent and uses @Relation to link the child list; Room runs a second query to fill it. Entities themselves cannot hold direct references to other entities.
A @Query references a column name that contains a typo. When is the error surfaced?
- At runtime, the first time that query actually executes
- Never; it just quietly returns null for that column
- At compile time, in Room's annotation-processing step
- Only if a migration test happens to execute that query
Answer: At compile time, in Room's annotation-processing step
Room validates @Query SQL against the schema at build time, so an unknown column or syntax error fails compilation rather than crashing later.
A method annotated @Insert(onConflict = OnConflictStrategy.REPLACE) inserts a row whose primary key already exists. What happens?
- The existing row is deleted, then the new row is inserted
- The insert is ignored and the existing row stays unchanged
- Room throws a SQLiteConstraintException on the conflict
- Both rows remain in the table with duplicate primary keys
Answer: The existing row is deleted, then the new row is inserted
REPLACE removes the conflicting row and inserts the new one. IGNORE would keep the existing row, and ABORT (the default) would throw on conflict.
Which statement about Room migrations is true?
- fallbackToDestructiveMigration preserves user data while rebuilding tables
- Auto-migrations generate correctly even when exportSchema is off
- A Migration's migrate() runs arbitrary Kotlin business logic instead of SQL
- @AutoMigration needs an AutoMigrationSpec for ambiguous renames or drops
Answer: @AutoMigration needs an AutoMigrationSpec for ambiguous renames or drops
Auto-migrations rely on exported schemas to handle clear changes, but ambiguous operations such as renaming or deleting tables/columns require an AutoMigrationSpec to disambiguate.
What must the class annotated with @Database look like?
- A final class with concrete DAO methods that you implement yourself
- Abstract class extending RoomDatabase with abstract DAO accessors
- An interface annotated @Dao that lists every query and accessor
- A data class whose properties are the entity types in the database
Answer: Abstract class extending RoomDatabase with abstract DAO accessors
The @Database class is an abstract class extending RoomDatabase with abstract accessor methods for each DAO; Room generates the concrete implementation at build time via KSP.
You call a non-suspend DAO query that returns List<User> directly from the main thread. What happens by default?
- Room moves the query to a background thread automatically, running fine
- The query blocks briefly, then returns null on the main thread
- Room throws IllegalStateException unless allowMainThreadQueries()
- It blocks the UI thread but only logs a strict-mode warning
Answer: Room throws IllegalStateException unless allowMainThreadQueries()
Synchronous queries on the main thread throw IllegalStateException to prevent UI jank; you must use suspend/Flow queries or explicitly opt in with allowMainThreadQueries().
An entity has @PrimaryKey(autoGenerate = true) on a Long id. How do you let Room assign new ids on insert?
- autoGenerate only works on String primary keys, not on Long ids
- You have to assign the id yourself; Room ignores autoGenerate here
- autoGenerate assigns a random UUID to each inserted row
- Leave the id at 0 and SQLite assigns the next rowId on insert
Answer: Leave the id at 0 and SQLite assigns the next rowId on insert
With autoGenerate = true on a Long key, leaving the id at 0 lets SQLite generate the next rowId; @Insert then returns that generated value as a Long.
An entity declares a @ForeignKey to a parent table with onDelete = ForeignKey.CASCADE. What does deleting a parent row do?
- Deleting the parent row automatically deletes the child rows that reference it
- Foreign keys are checked only at compile time and have no runtime effect
- CASCADE copies the parent row into each child row
- Room enforces foreign keys on every relationship even without declaring @ForeignKey
Answer: Deleting the parent row automatically deletes the child rows that reference it
onDelete = CASCADE propagates the parent deletion to all referencing child rows; foreign key constraints are real runtime constraints that you must opt into with @ForeignKey.
What is the correct way to model a many-to-many relationship (e.g. Students and Courses) in Room?
- Store comma-separated ids in each entity and split them with a type converter
- Define a junction entity, then use @Relation with associateBy = @Junction
- Room creates the join table automatically from two separate @Relation fields
- Add a List of the other entity directly inside each @Entity class
Answer: Define a junction entity, then use @Relation with associateBy = @Junction
Many-to-many requires an associative (junction) entity holding both keys, and Room links the two sides via @Relation with associateBy = @Junction pointing at that entity.
Why is it recommended to annotate a DAO method that returns an @Relation POJO with @Transaction?
- @Transaction has no real effect on read-only queries like this
- @Transaction is only valid on @Insert or @Update methods
- Room's multiple relation SELECTs then run atomically and stay consistent
- @Transaction automatically converts the method into a suspend function
Answer: Room's multiple relation SELECTs then run atomically and stay consistent
Fetching a parent plus its @Relation children involves several separate queries; wrapping them in @Transaction prevents another write from interleaving and producing an inconsistent combined result.
What can a method annotated @Update return?
- The rowId of the updated row as a Long value
- An Int with the number of rows actually updated
- Nothing; @Update methods must always return Unit
- A Boolean saying whether any row matched at all
Answer: An Int with the number of rows actually updated
@Update can optionally return an Int with the count of rows updated (matched by primary key); @Insert is the one that returns rowId Long values.