Room 持久化库:Android 本地 SQLite 抽象

FreeGuideOnline 最新 2026-06-17

Room 是什么?

Room 是 Android Jetpack 组件之一,旨在 SQLite 基础上提供一个抽象层。它简化了数据库操作,帮你处理模板代码,并在编译时校验 SQL 语句的正确性。

为什么选择 Room?

  • 编译期 SQL 校验,避免运行时错误。
  • 与 LiveData、Flow、RxJava 等可观察类型无缝集成。
  • 减少样板代码,提供注解驱动的 API。
  • 支持数据库迁移与类型转换器。

Room 的核心三要素

Room 主要由三个部分组成:

  1. Entity – 表示数据库中的一张表。
  2. DAO(Data Access Object) – 提供操作数据库的方法。
  3. Database – 持有所有 DAO 和 Entity 的抽象类,负责数据库创建和版本管理。

三者关系如下:
Database 包含多个 Entity,并暴露对应的 DAO 用于查询。

快速上手:7 步搭建 Room 数据库

步骤 1:添加依赖

在模块级 build.gradle 中添加:

plugins {
    id 'kotlin-kapt' // 如果使用 Kotlin
}

dependencies {
    def room_version = "2.6.1" // 请使用最新版本

    implementation "androidx.room:room-runtime:$room_version"
    annotationProcessor "androidx.room:room-compiler:$room_version" // Java
    kapt "androidx.room:room-compiler:$room_version" // Kotlin

    // 可选 - 与 Kotlin 协程 / Flow 集成
    implementation "androidx.room:room-ktx:$room_version"
}

步骤 2:创建 Entity

Entity 表示数据库中的一张表。用 @Entity 注解标注数据类,并定义主键。

import androidx.room.Entity
import androidx.room.PrimaryKey

@Entity(tableName = "users")
data class User(
    @PrimaryKey(autoGenerate = true) val id: Int = 0,
    val name: String,
    val age: Int,
    val isActive: Boolean = true
)
  • tableName 自定义表名,默认为类名。
  • @PrimaryKey 声明主键,autoGenerate 使其自增。
  • 支持使用 @ColumnInfo(name = "custom_name") 自定义列名。

步骤 3:创建 DAO

DAO 负责操作数据库,通过接口或抽象类定义方法,用注解标识 SQL。

import androidx.room.*
import kotlinx.coroutines.flow.Flow

@Dao
interface UserDao {
    @Insert(onConflict = OnConflictStrategy.REPLACE)
    suspend fun insertUser(user: User)

    @Update
    suspend fun updateUser(user: User)

    @Delete
    suspend fun deleteUser(user: User)

    @Query("SELECT * FROM users WHERE id = :userId")
    suspend fun getUserById(userId: Int): User?

    @Query("SELECT * FROM users ORDER BY name ASC")
    fun getAllUsers(): Flow<List<User>> // 返回 Flow 实现观察
}
  • @Insert@Update@Delete 提供便捷增删改。
  • @Query 可编写原生 SQL,支持编译期校验。
  • 返回类型可以是 FlowLiveDatasuspend 函数。

步骤 4:创建 Database 类

定义抽象类继承 RoomDatabase,列出所有 Entity 和抽象 DAO。

import androidx.room.Database
import androidx.room.RoomDatabase

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

步骤 5:实例化数据库

推荐在 Application 类中使用单例模式创建数据库,避免多次初始化。

import android.app.Application
import androidx.room.Room

class MyApplication : Application() {
    companion object {
        lateinit var database: AppDatabase
    }

    override fun onCreate() {
        super.onCreate()
        database = Room.databaseBuilder(
            applicationContext,
            AppDatabase::class.java,
            "app_database"
        ).build()
    }
}

步骤 6:执行数据库操作

在协程作用域中调用 DAO 方法。

class UserRepository(private val userDao: UserDao) {
    val allUsers: Flow<List<User>> = userDao.getAllUsers()

    suspend fun addUser(user: User) {
        userDao.insertUser(user)
    }
}

步骤 7:观察数据

在 Activity 或 Fragment 中使用 Flow 或 LiveData 观察数据变化。

lifecycleScope.launch {
    repository.allUsers.collect { users ->
        // 更新 UI
        adapter.submitList(users)
    }
}

进阶知识

类型转换器 (TypeConverter)

Room 默认只支持基本数据类型。存储复杂数据(如 Date、List)需要自定义类型转换器。

class Converters {
    @TypeConverter
    fun fromTimestamp(value: Long?): Date? {
        return value?.let { Date(it) }
    }

    @TypeConverter
    fun dateToTimestamp(date: Date?): Long? {
        return date?.time
    }
}

Database 中声明:

@Database(entities = [User::class], version = 1, exportSchema = false)
@TypeConverters(Converters::class)
abstract class AppDatabase : RoomDatabase() { ... }

定义表关系

Room 不支持直接存储对象引用,但你可以通过嵌套类或关联查询实现关系。

示例:一对多关系

// User 表不变
data class UserWithPets(
    @Embedded val user: User,
    @Relation(
        parentColumn = "id",
        entityColumn = "ownerId"
    )
    val pets: List<Pet>
)

@Query("SELECT * FROM users")
suspend fun getUsersWithPets(): List<UserWithPets>

数据库迁移

修改 Entity 结构后需提升 version 并实现 Migration

val MIGRATION_1_2 = object : Migration(1, 2) {
    override fun migrate(db: SupportSQLiteDatabase) {
        db.execSQL("ALTER TABLE users ADD COLUMN last_login INTEGER")
    }
}

Room.databaseBuilder(applicationContext, AppDatabase::class.java, "app_database")
    .addMigrations(MIGRATION_1_2)
    .build()

若不在意数据丢失,可使用 fallbackToDestructiveMigration(),但不推荐生产环境。

测试 Room 数据库

Room 提供内存数据库快速测试。

@RunWith(AndroidJUnit4::class)
class UserDaoTest {
    private lateinit var database: AppDatabase
    private lateinit var userDao: UserDao

    @Before
    fun setUp() {
        val context = ApplicationProvider.getApplicationContext<Context>()
        database = Room.inMemoryDatabaseBuilder(context, AppDatabase::class.java).build()
        userDao = database.userDao()
    }

    @After
    fun tearDown() {
        database.close()
    }

    @Test
    fun insertAndGetUser() = runTest {
        val user = User(name = "Test", age = 25)
        userDao.insertUser(user)
        val result = userDao.getUserById(1)
        assertEquals("Test", result?.name)
    }
}

最佳实践建议

  • 为每个实体单独设计 DAO 接口,保持职责单一。
  • 使用 FlowLiveData 实现响应式数据层。
  • 避免在主线程执行数据库操作(除非使用 allowMainThreadQueries(),但强烈不推荐)。
  • 生产环境务必规划好迁移策略。
  • 善用 @Transaction 确保多表操作的原子性。
  • 利用 @RewriteQueriesToDropUnusedColumns 等注解优化查询。
  • 使用 @ForeignKey 定义外键约束,保证数据完整性。

总结

Room 通过简洁的注解和编译期检查,将 Android 本地数据库开发的复杂度降到最低。掌握 Entity、DAO 和 Database 三大支柱后,你可以快速构建出可靠、可维护的持久化层。结合协程和 Flow,更能实现流畅的响应式数据流。