数据层
数据层 · 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-runtime 和 room3-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 驱动自动更新;同步查询禁止主线程。
- 单例复用;破坏式重建只用于可丢弃数据。