Room 数据库简介:架构、SQLite 关系与三大组件
做 Android 本地存储,绕不开 Room。我个人刚接触时也纳闷:直接用 SQLite 不香吗?后来才发现,SQLite 那套手动管理游标、写 SQL 模板的活儿,在复杂项目里简直就是噩梦。Room 本质上是对 SQLite 的一层封装,但它带来的不是“多一层麻烦”,而是帮你把脏活累活都干了。
Room 与 SQLite:到底差在哪?
说直白点,SQLite 是数据库引擎,Room 是上层框架。你写的 SQL 最终还是跑在 SQLite 上。那为什么还要用 Room?我遇到过好几次团队里有人直接裸写 SQLiteHelper,结果数据库升级时漏了一个字段,整个应用闪退——嗯,这种坑踩过一次就够了。
Room 提供了编译时 SQL 校验、自动生成样板代码、与 LiveData / Flow 无缝集成。说白了,它让你用面向对象的方式操作数据库,不用手动管游标、不用重复写那些烦人的 ContentValues。下面这张图能帮你快速理清 Room 的定位:
三大核心组件:Entity、DAO、Database
理解了整体关系,我们再逐个拆解。我习惯把 Entity 看作“表结构的 Kotlin/Java 化身”,DAO 是“你写 SQL 的地方”,Database 则是“把这些串起来的胶水”。
1. Entity —— 数据表映射
每一个 Entity 类对应一张数据库表,字段对应表的列。你只需要用注解标注主键,简单得离谱:
@Entity(tableName = "users")
data class User(
@PrimaryKey val id: Long,
@ColumnInfo(name = "full_name") val name: String,
val age: Int
)
我当年踩过一个坑:忘了加 @ColumnInfo 注解,Room 直接用 Kotlin 变量名做列名,结果后端返回的是 full_name,映射不上。嗯,所以字段名跟表列名不一样时,一定记得显式声明。
@Ignore 注解跳过某些字段,比如一个临时计算的年龄层。Room 不会把它存入数据库。
2. DAO —— 数据访问对象
DAO 是一个接口或抽象类,你在里面定义各种数据库操作方法,用注解标注 SQL 语句。Room 在编译时会自动帮你生成实现类。看个例子:
@Dao
interface UserDao {
@Query("SELECT * FROM users WHERE age > :minAge")
suspend fun getUsersOlderThan(minAge: Int): List<User>
@Insert
suspend fun insertUser(user: User)
@Delete
suspend fun deleteUser(user: User)
}
注意这里的 suspend 关键字——配合协程,数据库操作切到 IO 线程,UI 线程不会被卡。我个人习惯所有 DAO 方法都用挂起函数,这样调用方不需要手动管理线程。
3. Database —— 数据库的入口
Database 是一个抽象类,继承自 RoomDatabase。你需要定义它关联的 Entity 列表、DAO 的抽象方法,以及版本号。构建时 Room 会帮你创建表、管理版本迁移:
@Database(
entities = [User::class, Address::class],
version = 1,
exportSchema = false
)
abstract class AppDatabase : RoomDatabase() {
abstract fun userDao(): UserDao
abstract fun addressDao(): AddressDao
}
记得在 Application 里单例初始化:
val db = Room.databaseBuilder(
context.applicationContext,
AppDatabase::class.java,
"app_database.db"
).build()
三者的协作流程
我把日常调用流程整理成下面几步,你看完应该就明白了:
- App 启动 → 通过
Room.databaseBuilder创建AppDatabase实例。 - 业务层 调用
database.userDao()拿到UserDao。 - DAO 方法 执行 SQL(Insert/Query/Update/Delete),底层调用
SQLiteDatabaseAPI。 - 返回结果:查询直接返回 Entity 对象或列表,插入/删除返回影响行数。
整个过程你不需要写一行 SQLiteOpenHelper 或 Cursor 代码。Room 在编译时生成的实现类帮你把这些全包了。
选型时的几点建议
结合我自己的项目经验,给你几个实用建议:
- 如果数据关系复杂(比如多表联查、复杂事务),Room 更合适。它的 Entity 和 DAO 天然支持关联查询。
- 如果只是存 key-value,DataStore 更轻量。Room 对简单键值对来说太重了。
- 数据库迁移用 Room 的
Migration类加版本号控制,永远不要手动改数据库文件名。 - 调试阶段可以在 databaseBuilder 后加
.fallbackToDestructiveMigration(),但上线前一定要去掉——你想想看,用户数据说没就没,这锅谁背?
exportSchema = true 打开(release 需关闭),Room 会输出 JSON schema 文件,方便查看表结构变更历史。配合版本对比,数据库迁移几乎不会出错。