Room3:编译期验证的数据库层 · AndroidX 源码指南
AAndroidX 源码指南
数据层
数据层 · androidx.room3

Room3:编译期验证的数据库层Room3 3.0.0-rc01

Entity、DAO、Database 与迁移的完整用法,含事务、Flow、Relation 和 KMP 差异。

最后更新 2026-08-01

最小结构

import androidx.room3.Entity
import androidx.room3.PrimaryKey
import androidx.room3.Dao
import androidx.room3.Database
import androidx.room3.Query
import androidx.sqlite.SQLiteDriver
import androidx.room3.roomDatabaseBuilder

@Entity
data class User(
    @PrimaryKey val id: Long,
    val name: String,
)

@Dao
interface UserDao {
    @Query("SELECT * FROM User ORDER BY name")
    fun observeAll(): Flow<List<User>>

    @Query("SELECT * FROM User WHERE id = :id")
    suspend fun findById(id: Long): User?

    @androidx.room3.Insert
    suspend fun insert(user: User)

    @androidx.room3.Delete
    suspend fun delete(user: User)
}

@Database(entities = [User::class], version = 1)
abstract class AppDatabase : RoomDatabase() {
    abstract fun userDao(): UserDao
}

KSP 必配:Room3 由编译器生成实现,build.gradle.kts 必须同时有 room3-runtimeroom3-compiler(KSP),否则运行时会找不到生成的 DAO 实现。

构建数据库

Room3 是 KMP 风格,用 roomDatabaseBuilder 而非旧版 Room.databaseBuilder

val db = roomDatabaseBuilder<AppDatabase>(
    sqliteDriver = AndroidSQLiteDriver(
        schema = AppDatabase.Schema,
        context = applicationContext,
        name = "app.db",
    ),
).build()

要点:

  • 需要显式传入 SQLiteDriver(Android 用 AndroidSQLiteDriver)。
  • AppDatabase.Schema 是编译生成的对象,Room3 用它验证 schema 一致性。
  • 数据库复用单例;多进程访问用 ConnectionPoolConfiguration 配置。

事务

DAO 方法标 @Transaction,方法体内所有数据库操作在单事务中执行:

@Dao
interface SongDao {
    @Insert
    suspend fun insert(song: Song)

    @Delete
    suspend fun delete(song: Song)

    @Transaction
    suspend fun replaceSong(newSong: Song, oldSong: Song) {
        insert(newSong)
        delete(oldSong)
    }
}

@Transaction 加在 SELECT 查询返回带 @Relation 对象 的方法上也很重要——关联字段是分开查询的,事务保证它们读到一致快照。

迁移

schema 变化必须升级 version 并提供 Migration:

val MIGRATION_1_2 = object : Migration(1, 2) {
    override suspend fun migrate(connection: SQLiteConnection) {
        connection.execSQL("ALTER TABLE User ADD COLUMN age INTEGER NOT NULL DEFAULT 0")
    }
}

val db = roomDatabaseBuilder<AppDatabase>(driver)
    .addMigrations(MIGRATION_1_2)
    .build()
  • addMigrations(vararg) 注册迁移链。
  • 没有可用迁移时,fallbackToDestructiveMigration()删除重建——只适合可丢弃缓存,不要作为用户数据的默认方案。
  • fallbackToDestructiveMigrationOnDowngrade() 只对降级生效。

Relation:一对多

data class UserWithPosts(
    @Embedded val user: User,
    @Relation(parentColumn = "id", entityColumn = "userId")
    val posts: List<Post>,
)

@Dao
interface UserDao {
    @Transaction
    @Query("SELECT * FROM User")
    fun observeUsersWithPosts(): Flow<List<UserWithPosts>>
}

@Relation 属性会被单独查询;@Transaction 保证一致性。

查询类型

返回类型行为
suspend fun x(): T一次性查询,挂起直到结果
Flow<List<T>>表变更时自动重新发射(InvalidationTracker 驱动)
fun x(): List<T>(非 suspend)同步阻塞,禁止主线程调用

Room 的 Flow 由 InvalidationTracker 在表数据变更时触发重查,是”数据库单一事实源”的关键:写库 → Flow 自动推送 → UI 更新。

常见陷阱

  • 主线程查询:同步 DAO 方法在主线程调用会崩;用 suspend/Flow。
  • 迁移缺失:升级 version 但没给 Migration → 崩溃(除非 fallbackToDestructive)。
  • 单例:数据库实例重复创建会重复打开文件,浪费连接。
  • KSP 未配置:生成的 AppDatabase_Impl 找不到,编译过但运行崩。
  • 对象不变量:data class 属性名必须与列名匹配(或用 @ColumnInfo)。

复制即用

// build.gradle.kts
// implementation("androidx.room3:room3-runtime:3.0.0-rc01")
// ksp("androidx.room3:room3-compiler:3.0.0-rc01")

import androidx.room3.Dao
import androidx.room3.Database
import androidx.room3.Entity
import androidx.room3.PrimaryKey
import androidx.room3.Query
import androidx.room3.RoomDatabase
import androidx.room3.roomDatabaseBuilder
import androidx.sqlite.SQLiteConnection
import androidx.sqlite.SQLiteDriver
import kotlinx.coroutines.flow.Flow

@Entity
data class User(
    @PrimaryKey val id: Long,
    val name: String,
)

@Dao
interface UserDao {
    @Query("SELECT * FROM User ORDER BY name")
    fun observeAll(): Flow<List<User>>

    @androidx.room3.Insert
    suspend fun insert(user: User)
}

@Database(entities = [User::class], version = 1)
abstract class AppDatabase : RoomDatabase() {
    abstract fun userDao(): UserDao
}

class DatabaseFactory(
    private val context: Context,
) {
    val db: AppDatabase by lazy {
        roomDatabaseBuilder<AppDatabase>(
            sqliteDriver = AndroidSQLiteDriver(
                schema = AppDatabase.Schema,
                context = context.applicationContext,
                name = "app.db",
            ),
        ).build()
    }
}

要点

  • Entity/DAO/Database 三件套 + KSP 编译器;编译期检查 SQL 与映射。
  • 事务用 @Transaction(含 Relation 查询);迁移必须幂等配套 version。
  • Flow 查询由 InvalidationTracker 驱动自动更新;同步查询禁止主线程。
  • 单例复用;破坏式重建只用于可丢弃数据。

相关页面