Jetpack Room 从入门到精通

Jetpack Room:从入门到精通

  • 概述
  • [一、为什么选择 Room?](#一、为什么选择 Room?)
  • 二、核心组件
    • [1. @Entity (实体类)](#1. @Entity (实体类))
    • [2. @Dao (数据访问对象)](#2. @Dao (数据访问对象))
    • [3. @Database (数据库)](#3. @Database (数据库))
  • 三、基本操作
    • [1. 添加依赖](#1. 添加依赖)
    • [2. 创建实体 (User.kt)](#2. 创建实体 (User.kt))
    • [3. 创建 DAO (UserDao.kt)](#3. 创建 DAO (UserDao.kt))
    • [4. 创建数据库 (AppDatabase.kt)](#4. 创建数据库 (AppDatabase.kt))
    • [5. 在 Activity/Fragment 中使用](#5. 在 Activity/Fragment 中使用)
  • 四、进阶:精通之路
    • [1. 复杂查询 (@Query)](#1. 复杂查询 (@Query))
    • [2. 返回自定义对象](#2. 返回自定义对象)
    • [3. 数据库关系](#3. 数据库关系)
      • [3.1 一对多](#3.1 一对多)
      • [3.2 多对多](#3.2 多对多)
    • [4. 异步与响应式编程](#4. 异步与响应式编程)
    • [5. 数据库迁移](#5. 数据库迁移)
    • [6. 数据库创建/打开回调](#6. 数据库创建/打开回调)
    • [7. 类型转换器 (@TypeConverter)](#7. 类型转换器 (@TypeConverter))
  • 五、使用建议与注意事项

概述

Room 是 Google 推出的 Android 官方持久化库,它在 SQLite 的基础上提供了一个抽象层,极大地简化了数据库操作。它通过编译时的 SQL 验证和注解,让开发者能够更安全、更高效地使用 SQLite。

一、为什么选择 Room?

1.对比原生 SQLite

  • 减少样板代码:无需手动编写 SQLiteOpenHelper、ContentValues、Cursor 解析等繁琐代码。
  • 编译时 SQL 验证:在编译阶段检查 SQL 语句的正确性,避免运行时崩溃。
  • 与 LiveData/Flow 集成:查询结果可以直接返回 LiveData 或 Flow,实现数据变化自动通知 UI。
  • 支持 Kotlin 协程:DAO 方法可以声明为 suspend 函数,完美集成协程。
  • 迁移支持:提供便捷的数据库版本迁移机制。
  • 官方推荐:Jetpack 组件,与 Android 生态深度集成。

2.对比greenDao

对比维度 Room GreenDao
开发公司 Google官方(Jetpack架构组件) GreenRobot(第三方开源库)
支持平台 Android,深度集成LiveData/ViewModel Android,轻量级ORM,兼容性广
数据库类型 SQLite抽象层,类型安全,编译时SQL验证 基于SQLite,代码生成策略,性能优化
API设计 注解驱动(@Entity/@Dao),支持RxJava/Flow 代码生成模式,自动生成DAO类,API简洁
性能表现 插入1441ms/查询411ms(华为Mate10测试) 插入2771ms/查询750ms(同条件测试),批量操作更快
缓存机制 LiveData/Flowable自动缓存,实时UI更新 内存高效映射,支持异步操作
数据类型支持 强类型安全,支持自定义类型转换器 基本类型支持,需手动处理复杂类型
事务支持 编译时事务验证,集成Jetpack架构 基础事务支持,需手动管理
社区与文档 官方文档完善,更新频繁,生态成熟 社区活跃,但更新较慢,文档分散
代码生成 运行时通过注解处理器生成DAO实现 编译时生成DAO类,减少样板代码
加密支持 需自定义实现或第三方库 原生支持数据库加密
学习曲线 需掌握Jetpack架构,注解配置较复杂 简单易用,快速上手,配置灵活
典型场景 MVVM架构项目,需要实时数据同步 高性能需求场景,批量数据操作

说明:

  • 表格对齐:使用 : 控制对齐方式(默认居左,:--: 居中,--: 居右)。
  • 标题加粗:通过 ** 标记对比维度标题,提升可读性。
  • 兼容性:可直接复制到支持 Markdown 的平台(如 GitHub、Typora、Notion 等)使用。

二、核心组件

Room 有三个主要组件:

1. @Entity (实体类)

代表数据库中的一张表。

使用 @Entity 注解标记一个数据类。

类中的每个属性(字段)默认对应表中的一列。

Kotlin 复制代码
@Entity(tableName = "users") // 指定表名
data class User(
    @PrimaryKey val uid: Int, // 主键
    @ColumnInfo(name = "first_name") val firstName: String?, // 指定列名
    @ColumnInfo(name = "last_name") val lastName: String?
)

2. @Dao (数据访问对象)

包含用于访问数据库的方法(增删改查)。

使用 @Dao 注解标记一个接口或抽象类。

DAO 是 Room 的核心,所有数据库操作都通过 DAO 完成。

Kotlin 复制代码
@Dao
interface UserDao {
    @Query("SELECT * FROM users")
    fun getAll(): List<User>

    @Query("SELECT * FROM users WHERE uid IN (:userIds)")
    fun loadAllByIds(userIds: IntArray): List<User>

    @Query("SELECT * FROM users WHERE first_name LIKE :first AND " +
           "last_name LIKE :last LIMIT 1")
    fun findByName(first: String, last: String): User

    @Insert
    fun insertAll(vararg users: User)

    @Update
    fun update(user: User)

    @Delete
    fun delete(user: User)
}

3. @Database (数据库)

作为持久化数据的底层连接的主要访问点。

必须是一个抽象类,并继承自 RoomDatabase。

在注解中指定 entities(实体类)和 version(数据库版本)。

Kotlin 复制代码
@Database(entities = [User::class], version = 1)
abstract class AppDatabase : RoomDatabase() {
    abstract fun userDao(): UserDao // 获取 DAO 实例

    companion object {
        @Volatile
        private var INSTANCE: AppDatabase? = null

        fun getDatabase(context: Context): AppDatabase {
            return INSTANCE ?: synchronized(this) {
                val instance = Room.databaseBuilder(
                    context.applicationContext,
                    AppDatabase::class.java,
                    "app_database" // 数据库文件名
                ).build()
                INSTANCE = instance
                instance
            }
        }
    }
}

三、基本操作

1. 添加依赖

在 app/build.gradle 文件中添加:

Gradle 复制代码
dependencies {
    def room_version = "2.6.1" // 请使用最新稳定版本

    implementation "androidx.room:room-runtime:$room_version"
    annotationProcessor "androidx.room:room-compiler:$room_version"
    // 如果使用 Kotlin 协程,还需要
    implementation "androidx.room:room-ktx:$room_version"
    // 如果使用 Kotlin,使用 kapt
    kapt "androidx.room:room-compiler:$room_version"
}

2. 创建实体 (User.kt)

Kotlin 复制代码
深色版本
@Entity(tableName = "users")
data class User(
    @PrimaryKey(autoGenerate = true) // 自增主键
    val id: Long,
    val name: String,
    val email: String
)

3. 创建 DAO (UserDao.kt)

Kotlin 复制代码
@Dao
interface UserDao {
    @Query("SELECT * FROM users")
    suspend fun getAllUsers(): List<User> // 使用 suspend 支持协程

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

    @Insert
    suspend fun insertUser(user: User): Long // 返回新插入行的主键

    @Update
    suspend fun updateUser(user: User)

    @Delete
    suspend fun deleteUser(user: User)
}

4. 创建数据库 (AppDatabase.kt)

Kotlin 复制代码
@Database(entities = [User::class], version = 1, exportSchema = false)
abstract class AppDatabase : RoomDatabase() {
    abstract fun userDao(): UserDao

    companion object {
        @Volatile
        private var INSTANCE: AppDatabase? = null

        fun getInstance(context: Context): AppDatabase {
            return INSTANCE ?: synchronized(this) {
                val instance = Room.databaseBuilder(
                    context.applicationContext,
                    AppDatabase::class.java,
                    "user_database"
                )
                // .addCallback(sRoomDatabaseCallback) // 可选:数据库创建/打开回调
                .build()
                INSTANCE = instance
                instance
            }
        }
    }
}

5. 在 Activity/Fragment 中使用

Kotlin 复制代码
深色版本
class MainActivity : AppCompatActivity() {
    private lateinit var userDao: UserDao

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val db = AppDatabase.getInstance(this)
        userDao = db.userDao()

        // 在协程中执行数据库操作
        lifecycleScope.launch {
            // 插入
            val userId = userDao.insertUser(User(0, "Alice", "alice@example.com"))
            Log.d("DB", "Inserted user with id: $userId")

            // 查询
            val users = userDao.getAllUsers()
            Log.d("DB", "All users: $users")
        }
    }
}

四、进阶:精通之路

1. 复杂查询 (@Query)

  • 参数绑定:使用 :paramName 绑定方法参数。
  • 集合参数:使用 IN (:ids) 查询集合。
  • 模糊查询:LIKE '%' || :name || '%'。
  • 排序与分页:ORDER BY, LIMIT, OFFSET。
  • 聚合函数:COUNT, SUM, AVG, MAX, MIN。
Kotlin 复制代码
@Query("SELECT * FROM users WHERE name LIKE :nameQuery ORDER BY name LIMIT :limit OFFSET :offset")
suspend fun searchUsers(nameQuery: String, limit: Int, offset: Int): List<User>

@Query("SELECT COUNT(*) FROM users")
suspend fun getUserCount(): Int

2. 返回自定义对象

查询结果可以映射到非实体类的数据类。

Kotlin 复制代码
data class UserNameAndEmail(
    val name: String,
    val email: String
)

@Dao
interface UserDao {
    @Query("SELECT name, email FROM users")
    suspend fun loadUserNamesAndEmails(): List<UserNameAndEmail>
}

3. 数据库关系

Room 支持一对一、一对多、多对多关系,但需要手动处理。

3.1 一对多

例如 User 有多个 Pet。

Kotlin 复制代码
深色版本
@Entity
data class Pet(
    @PrimaryKey val petId: Long,
    val name: String,
    val userId: Long // 外键,关联 User.id
)

data class UserWithPets(
    @Embedded val user: User,
    @Relation(
        parentColumn = "id",
        entityColumn = "userId"
    )
    val pets: List<Pet>
)

@Dao
interface UserDao {
    @Transaction
    @Query("SELECT * FROM User")
    suspend fun getUsersWithPets(): List<UserWithPets>
}

3.2 多对多

需要一个中间表(Junction Table)。

Kotlin 复制代码
@Entity(primaryKeys = ["userId", "bookId"])
data class UserBookCrossRef(
    val userId: Long,
    val bookId: Long
)

@Entity
data class Book(
    @PrimaryKey val bookId: Long,
    val title: String
)

data class UserWithBooks(
    @Embedded val user: User,
    @Relation(
        entity = Book::class,
        parentColumn = "id",
        entityColumn = "bookId",
        associateBy = Junction(UserBookCrossRef::class)
    )
    val books: List<Book>
)

4. 异步与响应式编程

返回 LiveData:数据变化时自动通知观察者。

Kotlin 复制代码
@Query("SELECT * FROM users ORDER BY name")
fun loadUsers(): LiveData<List<User>> // 不再是 suspend

返回 Flow:更强大的响应式流,支持协程。

Kotlin 复制代码
@Query("SELECT * FROM users ORDER BY name")
fun getUsersFlow(): Flow<List<User>>

在协程作用域中收集:

Kotlin 复制代码
lifecycleScope.launch {
    userDao.getUsersFlow().collect { users ->
        // 更新 UI
    }
}

5. 数据库迁移

当数据库结构变化(如添加列、修改表)时,需要升级版本并提供迁移策略。

Kotlin 复制代码
val MIGRATION_1_2 = object : Migration(1, 2) {
    override fun migrate(database: SupportSQLiteDatabase) {
        database.execSQL("ALTER TABLE users ADD COLUMN last_updated INTEGER NOT NULL DEFAULT 0")
    }
}

// 在构建数据库时添加
Room.databaseBuilder(context, AppDatabase::class.java, "database")
    .addMigrations(MIGRATION_1_2)
    .build()

6. 数据库创建/打开回调

Kotlin 复制代码
private val sRoomDatabaseCallback = object : RoomDatabase.Callback() {
    override fun onOpen(db: SupportSQLiteDatabase) {
        super.onOpen(db)
        // 数据库打开时执行
    }

    override fun onCreate(db: SupportSQLiteDatabase) {
        super.onCreate(db)
        // 数据库创建时执行,可预填充数据
    }
}

7. 类型转换器 (@TypeConverter)

将复杂对象(如 Date, List, 自定义对象)存储为数据库支持的类型(如 Long, String)。

Kotlin 复制代码
深色版本
class Converters {
    @TypeConverter
    fun fromTimestamp(value: Long?): Date? {
        return value?.let { Date(it) }
    }

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

// 在数据库类中注册
@Database(entities = [User::class], version = 1)
@TypeConverters(Converters::class)
abstract class AppDatabase : RoomDatabase() {
    // ...
}

五、使用建议与注意事项

1.使用建议

Room 通过注解和编译时代码生成,极大地简化了 Android 上的 SQLite 操作。从定义实体、DAO 到构建数据库,整个过程清晰、类型安全。结合协程、LiveData 和 Flow,可以构建出响应迅速、用户体验良好的应用。

精通 Room 的关键在于:

  • 熟练掌握 @Query 的各种用法。
  • 理解并正确处理数据库关系。
  • 灵活运用 LiveData 和 Flow 实现响应式 UI。
  • 掌握数据库迁移和类型转换器。
  • 遵循最佳实践,编写高效、可靠的数据库代码。

2.注意事项

  • 不要在主线程执行数据库操作:Room 会抛出 IllegalStateException。使用 suspend 函数配合协程,或返回 LiveData/Flow。

  • 使用单例模式:数据库实例应全局唯一,避免频繁创建和销毁。

  • 合理设计实体和关系:避免过度复杂的关系查询。

  • 谨慎处理迁移:测试迁移脚本,避免数据丢失。

  • 利用编译时检查:Room 会在编译时报错,及时修复 SQL 语法错误。

  • 考虑数据量:对于超大数据集,考虑分页加载。

  • 使用 @Transaction:确保多个数据库操作的原子性。

相关推荐
qh0526wy2 小时前
DUCKLAKE 同步数据库
数据库·oracle
^辞安2 小时前
什么是Mvcc
java·数据库·mysql
2501_915918412 小时前
iOS 26 软件兼容性大检查,哪些 App 出问题、API 变动要注意、旧功能不支持兼容性测试全流程
android·ios·小程序·https·uni-app·iphone·webview
2501_915909063 小时前
iOS 26 系统流畅度深度评测 Liquid Glass 动画滑动卡顿、响应延迟、机型差异与 uni-app 优化策略
android·ios·小程序·uni-app·cocoa·iphone·webview
2501_915909063 小时前
App 代上架全流程解析 iOS 应用代上架服务、苹果应用发布步骤、ipa 文件上传与 App Store 审核经验
android·ios·小程序·https·uni-app·iphone·webview
2501_916013743 小时前
iOS 开发指南全解析 从入门到应用上架、Xcode 使用教程、ipa 打包上传与 App Store 审核实战经验
android·macos·ios·小程序·uni-app·iphone·xcode
咖啡の猫3 小时前
Android开发-Application
android
00后程序员张3 小时前
iOS 混淆与反调试反 Hook 实战,运行时防护、注入检测与安全加固流程
android·安全·ios·小程序·uni-app·iphone·webview
2501_915918413 小时前
iOS 上架流程详细指南 苹果应用发布步骤、ipa 文件上传 打包上架实战经验
android·ios·小程序·https·uni-app·iphone·webview