CHAPTER 21
Beginner
Local Databases with Room (SQLite)
Updated: May 16, 2026
35 min read
# CHAPTER 21
Local Databases with Room (SQLite)
1. Introduction
In Chapter 20, we downloaded user data from the internet. However, mobile devices frequently lose connection—when users step into an elevator, ride the subway, or activate airplane mode. If your app relies *only* on the internet, it becomes completely useless offline. Professional applications download data once and cache it locally on the phone's hard drive. Historically, Android developers wrote raw, error-prone SQLite queries to achieve this. Today, Google provides the Room Persistence Library. Room provides an abstraction layer over SQLite, turning complex database tables into simple Kotlin Objects and validating your SQL queries at compile time. In this chapter, we will master Local Databases with Room.2. Learning Objectives
By the end of this chapter, you will be able to:- Explain the architectural importance of offline-first local data caching.
- Configure Room Database dependencies within Gradle.
-
Define a database table utilizing the
@Entityannotation.
-
Execute SQL queries (CRUD operations) safely utilizing a Data Access Object (
@Dao).
-
Construct the central
@Databaseclass to manage connections.
- Execute database transactions securely utilizing Coroutines.
3. Setting Up Room in Gradle
Room requires several components, including the Kotlin Symbol Processing (KSP) plugin to generate the complex background code for you. First, add the KSP plugin to your project-levelbuild.gradle.kts:
*(Note: Setting up KSP can vary slightly depending on your Android Studio version. Follow the official documentation if you encounter sync errors!)*
Add these to your build.gradle.kts (Module :app):
kotlin
4. Step 1: The @Entity (The Table)
A database needs a Table. In Room, a Table is simply a Kotlin Data Class annotated with @Entity.
kotlin
5. Step 2: The @Dao (The Queries)
The DAO (Data Access Object) is an Interface where you define *how* you want to interact with the table. You don't write the complex connection code; Room reads your annotations and generates the code for you!
Notice the suspend keyword! Database operations are slow and must be executed on Coroutine background threads.
kotlin
*Magic Feature:* By returning LiveData from a DAO query, Room automatically observes the database. If you insert a new user, Room instantly pushes the updated list to the Activity without you writing a second query!
6. Step 3: The @Database (The Core Engine)
Finally, we must construct the actual database that holds our Entities and DAOs. We build this as an abstract class. We also use a Singleton pattern to ensure we don't accidentally open 5 separate connections to the database, which causes severe memory leaks.
kotlin
7. Step 4: Using the Database in the ViewModel
Just like Retrofit, we NEVER run database queries on the Main Thread. We inject our DAO into the ViewModel and execute the inserts insideviewModelScope.launch(Dispatchers.IO).
kotlin
8. Database Migrations
If you release your app withversion = 1, and a month later you update UserEntity to add a val profilePicture: String column, the app will instantly crash for all existing users! SQLite databases cannot magically change their structure without instructions.
You must update the @Database(version = 2) and provide a Migration Object detailing the SQL ALTER TABLE command. For beginners, if you change your Entity during development, simply uninstall the app from the emulator to wipe the old database.
9. Common Mistakes
-
Main Thread Crashes: Attempting to call
dao.insertUser()directly from a Button click on the Main Thread. Room is designed to be bulletproof; if it detects you executing a synchronous query on the UI thread, it will purposefully crash the application with anIllegalStateExceptionto prevent ANRs.
-
Forgetting
suspend: If you forget to add thesuspendkeyword to your@Insertor@Deletefunctions in the DAO, Kotlin will allow you to call them on the Main Thread, triggering the crash mentioned above.
10. Best Practices
- Single Source of Truth: A robust architecture utilizes Room as the Single Source of Truth. When the app opens, it fetches the API (Retrofit). It does NOT display the API data directly. Instead, it saves the API data into Room. The UI *only* observes the Room Database. This guarantees seamless offline functionality.
11. Exercises
-
1.
Create a
NoteEntitydata class with an auto-generatingid(Int),title(String), andcontent(String).
-
2.
Create a
NoteDaointerface. Write an@Insertfunction, and an@Queryfunction that selects all notes ordered by title alphabetically.
12. Coding Challenges
Challenge: Implement a Search feature. Inside your DAO, write a custom@Query that utilizes the SQL LIKE operator to search the database for a specific string.
*(Hint: @Query("SELECT * FROM users_table WHERE name LIKE :searchQuery") and pass searchQuery: String into the function parameters).*
13. MCQ Quiz with Answers
Question 1
What is the structural purpose of the @Entity annotation within the Android Room Persistence Library?
Question 2
Why does the Room architecture aggressively throw an IllegalStateException if a developer attempts to execute a DAO @Insert operation without utilizing Dispatchers.IO or an asynchronous background thread?
14. Interview Questions
-
Q: Explain the structural mechanics of the Room DAO returning
LiveData. How does this specific implementation facilitate reactive UI synchronization without manual database polling?
-
Q: Describe the catastrophic consequences of incrementing a Room Database
versionwithout providing a correspondingMigrationobject. How does the concept of Schema integrity apply here?
- Q: Contrast the persistence capabilities of SharedPreferences/DataStore versus a Room Database. When is it architecturally inappropriate to utilize Room?
15. FAQs
Q: Can Room store complex objects, like aList<String> inside my Entity?
A: No! SQLite can only store primitives (Int, String, Boolean). If you try to store a custom object or a List, Room will throw an error. You must utilize a Room TypeConverter to convert your List into a JSON String before saving, and back into a List when reading.
16. Summary
In Chapter 21, we secured our application's offline capabilities. We transitioned from volatile network reliance to permanent local persistence utilizing the Room Database. We constructed SQLite Tables via Kotlin@Entity Data Classes, automated complex query execution using DAOs, and engineered a robust Database Singleton Engine. By executing operations asynchronously and observing the database reactively via LiveData, we laid the foundation for enterprise-grade, offline-first architectures.