跪拜 Guibai
← All articles
Android Jetpack · Kotlin · Database

Room in Practice: Compile-Time SQL Checks and a Full CRUD Notes Database for Android

By Android_实战之道 ·
Read original on juejin.cn ↗ Google Translate ↗ Alt translation

Runtime 'table not found' and column-name crashes move to compile time, and a stable repository boundary keeps persistence swaps invisible to the UI. Room is the default persistence recommendation for modern Android, and this migration path is the one most production notes-style apps take.

Summary

Room replaces hand-written SQLite helpers, cursor parsing, and string-concatenated SQL with an annotation-driven layer. @Entity marks a Kotlin data class as a table, @Dao declares the operations, and @Database owns the connection and exposes DAO instances. SQL syntax, table names, field names, and return types are validated during compilation, so a typo in a query fails the build instead of crashing at runtime.

The notes-database example puts those pieces to work. A Note entity uses an auto-generated primary key and a custom table name; its DAO provides insert, update, delete, ordered list, find-by-id, and clear-all operations. Suspend functions let Room run database work off the main thread. The AppDatabase is a thread-safe singleton built with Application Context, and TypeConverters bridge Date and List<String> values to SQLite-supported types.

The repository layer swaps in-memory data for Room while keeping method signatures stable, so ViewModel and UI code stay untouched. Conflict strategies, compile-time validation compared with raw SQLite failure modes, and setup traps such as missing kapt or Activity Context make up the rest of the material.

Takeaways
— Native SQLite requires manually managing helper classes, cursor parsing, and SQL string concatenation; Room generates all of that from @Entity, @Dao, and @Database annotations.
— Room checks SQL syntax, table names, field names, and return types at compile time, turning runtime crashes into build errors.
— The three core Room components are the @Entity table mapping, the @Dao data-access interface, and the @Database singleton holder.
— @Insert with OnConflictStrategy.REPLACE replaces an existing row on primary-key conflict, IGNORE keeps the old row, and ABORT throws.
— Declaring DAO methods as suspend lets Room run database operations on a background thread automatically.
— SQLite stores only basic types, so @TypeConverter methods convert Date to Long and List<String> to JSON strings.
— The database singleton uses @Volatile, synchronized, and Application Context to avoid leaks and concurrency problems.
— The repository's public API stayed identical after swapping in-memory storage for Room, so the ViewModel and UI required no edits.
— After the switch, notes survive process death and screen rotation without extra code.
— The implementation uses Room 2.6.1, Kotlin kapt for annotation processing, and Version Catalog dependency declarations.
Conclusions

Compile-time SQL validation is the strongest practical reason to use Room: the comparison table shows raw SQLite only reports errors at runtime, while Room catches them during the build.

The zero-change repository swap demonstrates the real value of layered architecture; ViewModel and UI code did not need to know that data storage moved from memory to a database.

Room keeps developers writing SQL instead of hiding it behind a fully abstracted ORM; @Query methods take hand-written SQL, so database control stays visible while tooling catches mistakes.

DAOs designed around suspend functions push thread handling into Room's coroutine integration, removing a common source of main-thread bugs without extra code.

Concepts & terms
Room
Google's SQLite abstraction library for Android. Annotations map Kotlin data classes to tables, and Room generates DAO implementations and validates SQL at compile time.
DAO (Data Access Object)
An interface annotated with @Dao that defines the app's database operations. Room generates the implementation, and the business layer uses it as the only database entry point.
OnConflictStrategy
A policy for insert or update conflicts: REPLACE overwrites the conflicting row, IGNORE keeps the existing row, and ABORT aborts the statement and throws.
@TypeConverter
A method annotated with @TypeConverter that converts a Kotlin type SQLite cannot store, such as Date or List, into a SQLite-supported type such as Long or String.
kapt
Kotlin annotation processing tool used by Room to generate code at compile time; without it, DAO and Database implementations are not produced.
From the discussion
Featured comments
亚雷

Deleting a note by its primary key is more reliable.

See top comments, translated →
Source: juejin.cn ↗ Google Translate ↗ Backup ↗