Room:什么是ORM库及SQLite操作

作者: IT Sectr 发布日期: 2026-03-12 阅读时间: 10 分钟

Room 是 Android Jetpack 中的 ORM 库,它在 SQLite 之上提供抽象层,用于在 Android 上处理本地数据库。根据官方文档 Android Developers, 2025Room 在编译时基于注解自动生成 DAO 实现,与直接使用 SQLiteOpenHelper 相比,消除了大约 70% 的样板代码。该库在编译阶段执行 SQL 查询验证,从而在应用程序在设备上运行之前发现语法错误。

要点

  • Room — Android Jetpack ORM 库,提供 SQLite 之上的抽象层,用于在 Android 应用中本地存储数据。
  • 三个主要组件:Entity(表描述)、DAO(数据操作)和 Database(数据库入口点)。
  • 编译阶段验证 SQL 查询 — 关键优势,可以在安装应用程序之前发现错误。
  • 内置支持 Flow、LiveData 和 RxJava,用于响应式跟踪数据库中的更改。
  • 迁移机制允许更新数据库架构,而不会丢失已保存的用户数据。

什么是 Room ORM 库?

Room 是 Android Jetpack 中的 ORM 库,由 Google 创建,用于简化 Android 平台上本地 SQLite 数据库的操作。它提供注解来描述数据模式,并在编译阶段自动生成 DAO 接口的实现。与直接使用 SQLiteOpenHelper 不同,Room 使开发人员免于编写大量样板代码来创建、打开和管理数据库连接。

该库在 Google I/O 2017 上作为 Android 架构组件的一部分推出。从那时起,Room 已成为本地数据存储的事实标准,在流行度上超过了 Android 的 GreenDAO 和 Realm 等解决方案。据 Google 数据,该库用于 Google Play 中发布的 60% 以上的处理设备本地数据的应用程序。

关键特性 — 使用注解处理器在编译阶段验证 SQL 查询。如果开发人员在 SQL 命令中出错,例如指定了不存在的列名,编译将在安装应用程序之前以错误结束。这与 SQLiteOpenHelper 方法完全不同,在这种方法中,此类错误仅在运行时才发现,通常是在生产环境中。

非标准类型的 TypeConverter

SQLite 只支持五种数据类型:TEXT、INTEGER、REAL、BLOB 和 NULL。但是在 Java 和 Kotlin 中使用了复杂类型:Date、List、Enum 和自定义对象。为了存储它们,Room 提供了 TypeConverters 机制 — 将复杂类型转换为 SQLite 可理解的原始类型的静态方法。例如,Date 对象转换为 Long(时间戳),List<String> 通过 Gson 或 Moshi 转换为 JSON 字符串。

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

val db = Room
    .databaseBuilder(context, AppDatabase::class.java, "app-db")
    .build()

要声明转换器,只需将 @TypeConverter 注解添加到静态方法,并在数据库级别的 @TypeConverters 注解中指定转换器类。Room 在每个 SQL 查询中读取和写入相应类型时自动应用转换器,无需手动调用转换方法。

Room 架构:三个关键组件

Room 由三个主要组件组成:Entity、DAO 和 Database。每个组件都扮演着严格定义的角色,并用相应的注解进行标注。它们一起形成了一个完整的数据访问层,将应用程序的业务逻辑与 SQLite 的实现细节隔离开来。

Entity — 数据库表

Entity 是一个数据类,描述数据库中一个表的结构。类的每个字段对应表的一列,数据库中的每一行对应类的一个实例。@Entity 注解告诉 Room 该类是一个表。带有 @PrimaryKey 注解的字段定义主键,可以是自增的或复合的。对于表之间的关系,使用 @ForeignKey,确保数据库级别的数据完整性。

kotlin
@Entity(tableName = "users")
data class User(
    @PrimaryKey(autoGenerate = true)
    val id: Int = 0,
    @ColumnInfo(name = "full_name")
    val name: String,
    val age: Int,
    val email: String
)

DAO — 数据操作

DAO(数据访问对象)是一个接口或抽象类,声明用于处理数据的操作:插入、读取、更新和删除。每个操作都用 @Insert、@Query、@Update 或 @Delete 进行注解。Room 在编译阶段自动生成此接口的实现。@Query 注解具有特殊价值,它接收 SQL 查询作为字符串并在构建阶段检查其正确性。

kotlin
@Dao
interface UserDao {
    @Insert
    suspend fun insert(user: User): Long

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

    @Query("SELECT * FROM users")
    fun getAllUsers(): Flow<List<User>>

    @Delete
    suspend fun delete(user: User)
}

Database — 入口点

Database 是一个抽象类,继承自 RoomDatabase,作为数据库的入口点。它包含所有 Entity 的列表,并提供获取 DAO 的抽象方法。该类用 @Database 注解,其中指定了架构版本和实体列表。数据库实例的创建通过 Room.databaseBuilder 完成,指定应用程序上下文、文件名和 Database 类。

Room 如何在底层与 SQLite 工作

Room 不取代 SQLite,而是作为抽象层在其之上工作。内部架构包括注解处理器、代码生成器和连接池。在编译阶段,注解处理器分析 Entity、DAO 和 Database 类,然后生成带有 _Impl 后缀的实现类。所有生成的类都放置在构建包中,开发人员无法直接看到。

编译阶段的代码生成 — Room 的核心机制。对于每个 DAO 接口,都会生成一个包含所有注解方法完整实现的类。来自 @Query 注解的 SQL 查询会检查正确性:处理器将列名与 Entity 字段匹配,并检查 SQL 语法。检测到错误时,编译会中断并显示清晰的消息。这在使用原始 SQLiteOpenHelper 时是不可能的,在那种情况下错误只在运行时出现。

编译阶段的代码生成

生成过程包括三个阶段。第一 — 模式验证:处理器检查 @Database 中列出的所有类是否为有效的 Entity。第二 — DAO 主体生成:为每个方法创建实现,使用内部 RoomSQLiteQuery 对象执行预准备查询。第三 — Database_Impl 类的生成,实现数据库的创建和打开以及所有 DAO 对象的初始化。

kotlin
class UserDao_Impl(private val __db: RoomDatabase) : UserDao {
    private val __insertionAdapter = __db
        .createInsertionAdapter(User::class, 0)

    override suspend fun insert(user: User): Long {
        __db.assertNotSuspendingTransaction()
        return __db.runInTransaction {
            __insertionAdapter.insertAndReturnId(user)
        }
    }
}

Room 不为数据库操作创建单独的线程池。默认情况下,查询在调用线程中执行,但有一个限制:读写会阻塞线程。对于异步工作,Room 通过 suspend 函数与 Kotlin 协程集成,通过返回值与 LiveData 集成,通过响应式包装器与 Flow 集成。这为开发人员针对特定任务选择架构解决方案提供了灵活性。

Room 在 Android 应用中的使用示例

让我们看一个使用 Room 创建笔记存储应用程序的实际示例。该应用程序包含一个带有 id、title、content 和 timestamp 字段的 Note 表。用户可以添加、查看和删除笔记。演示使用了协程进行异步操作。

配置 Gradle 依赖

要将 Room 连接到 Android 项目,需要在应用程序模块的 build.gradle 文件中添加依赖项。Room 需要三个组件:运行时库、kapt 注解处理器和可选的协程支持。库版本在 room_version 变量中指定,便于更新。从 Room 2.4.0 开始,KSP 作为 kapt 的替代方案被支持,具有更高的构建速度。

groovy
dependencies {
    def room_version = "2.6.1"
    implementation "androidx.room:room-runtime:$room_version"
    kapt "androidx.room:room-compiler:$room_version"
    implementation "androidx.room:room-ktx:$room_version"
    // 可选:测试
    testImplementation "androidx.room:room-testing:$room_version"
}

配置依赖项后,创建三个文件:Note Entity、NoteDao 接口和 AppDatabase 类。Note Entity 包含带有 @PrimaryKey 和 @ColumnInfo 注解的字段。DAO 提供插入、获取列表和删除的方法。Database 通过 @Database 注解连接 Entity 和 DAO。

kotlin
@Entity(tableName = "notes")
data class Note(
    @PrimaryKey(autoGenerate = true)
    val id: Int = 0,
    val title: String,
    val content: String,
    @ColumnInfo(name = "created_at")
    val timestamp: Long = System.currentTimeMillis()
)

@Dao
interface NoteDao {
    @Insert
    suspend fun insert(note: Note)

    @Query("SELECT * FROM notes ORDER BY created_at DESC")
    fun getAllNotes(): Flow<List<Note>>

    @Delete
    suspend fun delete(note: Note)
}

AppDatabase 文件声明为继承 RoomDatabase 的抽象类。在 @Database 注解中,列出了当前版本的所有 Entity 和架构版本号。使用单例模式通过 Room.databaseBuilder 的 build 方法获取实例,并传入应用程序上下文。缓存数据库实例可防止可能导致内存泄漏的多次创建。

Room 中的数据库迁移

迁移 是 Room 中的一种机制,用于在更新应用程序时更改数据库架构而不丢失现有数据。当用户安装带有修改后的 Entity 的新版本时,Room 检测到版本不匹配并执行指定的迁移步骤。如果没有迁移,数据库将被删除并重新创建,导致所有已保存的用户数据丢失。

迁移由 Migration 类描述,它接收数据库的初始和最终版本。在 migrate 方法内部,执行 ALTER TABLE 或 CREATE TABLE SQL 查询以更改架构。Room 无法自动检测架构更改 — 开发人员必须为每个 Entity 更改手动编写迁移。从 Room 2.4.0 版本开始,提供了实验性的 autoMigrations 功能用于自动生成迁移。

使用 autoMigrations 自动迁移

autoMigrations 功能允许 Room 基于 Entity 版本之间的差异自动生成迁移。要使用它,只需在 @Database 中添加 @AutoMigration 注解并指定架构导出到 JSON。Room 比较相邻版本的架构并生成必要的 ALTER 查询。然而 autoMigrations 只支持向后兼容的更改:添加列、创建索引和具有兼容转换的类型更改。

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

val db = Room
    .databaseBuilder(context, AppDatabase::class.java, "app-db")
    .addMigrations(MIGRATION_1_2)
    .build()

添加复杂更改时,例如重命名列或合并表,需要使用中间表进行手动迁移。典型场景:创建一个具有旧架构的临时表,将数据从旧表复制到新表并进行转换,删除旧表并重命名临时表。Room 保证所有迁移都在一个事务中执行,并且在发生错误时更改将完全回滚。

常见问题

Room 与 SQLiteOpenHelper 有何不同?

Room 提供带有注解和编译阶段 SQL 验证的 ORM 抽象,而 SQLiteOpenHelper 需要手动编写所有查询和管理连接。Room 自动生成 CRUD 操作的代码,并与 Android 架构组件(包括 LiveData 和 Flow)集成。

Room 支持哪些数据类型?

Room 支持所有 Java 原始类型:Int、Long、Boolean、Float、Double,以及 String、ByteArray 和 Date。对于复杂类型,如 List 或 Enum,使用 TypeConverters — 将非标准类型转换为 SQLite 支持格式的静态转换方法。

可以在没有协程的情况下使用 Room 吗?

是的,Room 支持没有协程的同步调用,但它们会阻塞执行的线程。对于异步工作,可以使用 LiveData 或 RxJava 代替协程。Google 建议在新项目中使用协程作为异步数据访问的主要方式。

没有迁移会怎样?

如果 Room 检测到数据库版本不匹配并且找不到合适的迁移,默认会抛出 IllegalStateException,并附带错误描述。开发人员可以使用 fallbackToDestructiveMigration 方法覆盖此行为,该方法将删除现有数据库并创建新数据库,导致所有数据丢失。

Room 如何处理表之间的关系?

Room 通过带有 @Embedded 注解的嵌套对象和带有 @Relation 注解的关系类支持关系。对于涉及表连接的复杂查询,使用自定义 POJO 类,其字段从带有 SQL JOIN 运算符的 @Query 结果中填充。

总结

  • Room — Android Jetpack ORM 库,在 SQLite 之上创建抽象层,用于在设备上方便地存储数据。
  • 架构基于三个组件:Entity(表模式)、DAO(操作)和 Database(入口点)。
  • 编译阶段验证 SQL 查询 — 消除查询中运行时错误的主要优势。
  • 内置支持 Flow、LiveData 和 RxJava,可以构建响应式架构,在数据更改时自动更新 UI。
  • Room 中的迁移确保数据库架构的无缝更新,不会丢失已保存的用户信息。
  • 该库通过 suspend 函数与 Kotlin 协程集成,简化了异步数据工作。
  • 对于新项目,Room 是 Google 官方推荐的 Android 本地数据存储解决方案。

我们将开发一款交钥匙移动应用程序

IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。

讨论项目

另请阅读