Jetpack Room 从入门到精通 - 实践

news/2025/9/24 11:54:01/文章来源:https://www.cnblogs.com/yxysuanfa/p/19108886

Jetpack Room:从入门到精通

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

概述

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

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

说明:

二、核心组件

Room 有三个主要组件:

1. @Entity (实体类)

代表数据库中的一张表。
使用 @Entity 注解标记一个数据类。
类中的每个属性(字段)默认对应表中的一列。

@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 完成。

@Dao
interface UserDao {
@Query("SELECT * FROM users")
fun getAll(): List
@Query("SELECT * FROM users WHERE uid IN (:userIds)")
fun loadAllByIds(userIds: IntArray): List
@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(数据库版本)。

@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 文件中添加:

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)

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

3. 创建 DAO (UserDao.kt)

@Dao
interface UserDao {
@Query("SELECT * FROM users")
suspend fun getAllUsers(): List // 使用 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)

@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 中使用

深色版本
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)

@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
@Query("SELECT COUNT(*) FROM users")
suspend fun getUserCount(): Int

2. 返回自定义对象

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

data class UserNameAndEmail(
val name: String,
val email: String
)
@Dao
interface UserDao {
@Query("SELECT name, email FROM users")
suspend fun loadUserNamesAndEmails(): List
}

3. 数据库关系

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

3.1 一对多

例如 User 有多个 Pet。

深色版本
@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
)
@Dao
interface UserDao {
@Transaction
@Query("SELECT * FROM User")
suspend fun getUsersWithPets(): List
}

3.2 多对多

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

@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
)

4. 异步与响应式编程

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

@Query("SELECT * FROM users ORDER BY name")
fun loadUsers(): LiveData> // 不再是 suspend

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

@Query("SELECT * FROM users ORDER BY name")
fun getUsersFlow(): Flow>

在协程作用域中收集:

lifecycleScope.launch {
userDao.getUsersFlow().collect { users ->
// 更新 UI
}
}

5. 数据库迁移

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

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. 数据库创建/打开回调

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)。

深色版本
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 的关键在于:

2.注意事项

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

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

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

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

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

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

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/915668.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

网站建设unohacha免费北京网站建设

导言 在 Rust 中,互斥器(Mutex)是一种用于在多个线程之间共享数据的并发原语。互斥器提供了一种安全的方式,允许多个线程访问共享数据,但每次只允许一个线程进行写操作。本篇博客将详细介绍 Rust 中互斥器的使用方法&…

不干胶网站做最好的wordpress关闭

应用场景:一个游戏可能会衍生出其他APP或小程序之类的软件,例如王者营地是王者荣耀的官方APP,王者营地提供资讯、赛事、社区、战绩等功能。所以游戏端会和衍生出来的软件端做一些数据互通。这里把软件端称为中台系统。 Get请求和Post请求的区…

自建个网站怎么做网站建设与管理是干什么的

Windows 下本地 Docker RAGFlow 部署指南 环境要求部署步骤1. 克隆代码仓库2. 配置 Docker 镜像加速(可选)3. 修改端口配置(可选)4. 启动服务5. 验证服务状态6. 访问服务7. 登录系统8. 配置模型8.1 使用 Ollama 本地模型8.2 使用在线 API 服务9. 开始使用10. 常见问题处理端…

ClickHouse index_granularity 详解 - 若

ClickHouse index_granularity 详解 什么是 index_granularity index_granularity 是ClickHouse中一个重要的性能配置参数,它定义了索引的粒度(granularity),即每多少个数据行会创建一个索引标记(index mark)。 …

PADS笔记

PADS笔记PCB设计流程准备--功能确定、元器件选型 元件库建立-元器件符号、器件封装 绘制原理图-根据电路功能,将元器件符号进行连接 导出网络表--将元器件的连接关系,以及元器件的信息导出一个文件,以方便导入到其他…

【2025最新教程】Claude Code国内使用_保姆级新手安装使用教程_最强AI编程工具

【2025最新教程】Claude Code国内使用_保姆级新手安装使用教程_最强AI编程工具什么是 Claude Code Claude Code 是 Anthropic 推出的一个 agentic 编码工具 (agentic coding tool),可以在命令行(terminal)中运行,或…

如何计算sequence粒度的负载均衡损失 - 教程

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

P13885 [蓝桥杯 2023 省 Java/Python A] 反异或 01 串

发现操作完后必定为一个回文串,并且至多消去区间一半数量的 \(1\),求最长回文串即可。

获取网站缩略图的asp代码wordpress的cms主题

防火墙在解决方案及典型项目中的应用 防火墙作为基础安全防护产品,在各种解决方案、业务场景中配套应用,本节给出各类方案资料链接方便查阅。 防火墙在华为网络解决方案中的应用 解决方案 文档 主要应用 CloudFabric云数据中心网解决方案 资料专区…

怎样的网站打开速度块北京房产网二手房出售

这一部分开始,我们将讲解Python中的组合数据类型,这里的知识十分基础而且重要,也已经与C语言的框架愈差愈远。 目录 序列和索引 1、概念 2、切片操作 3、序列的其他操作 列表 1、概念 2、创建与删除 3、列表的操作 4、列表生成式 …

网站开发 有哪些优化功能4p营销理论

随着电商行业的快速发展,个性化服务已经成为提升用户体验和增加用户粘性的关键。基于API的电商平台数据定制和推荐系统是实现这一目标的重要技术手段。 未来,个性化服务可能会朝以下几个方向发展: 更精准的用户画像:通过API接口…

clickhouse轻量级更新 - 若

轻量级更新(Lightweight Updates)是ClickHouse中的一个重要特性,让我详细解释一下: 什么是轻量级更新 轻量级更新是ClickHouse提供的一种高效的UPDATE机制,它允许在不重写整个数据块的情况下更新数据。 传统更新 …

西电PCB设计指南第3章学习笔记

西电PCB设计指南第3章学习笔记 三、PCB的设计与规范画图前的准备确定外轮廓(在机械层核对尺寸,安装孔位,定义PCB边界轮廓)设置layerstack(节点厚度和属性)话说我好像安装了专门算这个的软件?嘿嘿嘿:happy:那么为…

Vitrualbox、kali、metaspolitable2下载安装

太多资源看不过眼,整理了几个下载比较快、安装教程比较实用的链接。 这里下的是Virtualbox7.2.2和7.2.2版本的扩展包、kali2025.3和metasplotable2,这里直接用的最新的kali和最新稳定版本的Virtualbox版本。 注意下载…

有域名了如何建网站ftp服务器上传不了wordpress

题解: 我发现拉格朗日乘数法真是个好东西。。 我是不会说我数学竞赛求最值都是用这个东西的 由于我不太会打那个符号就用li代表通常偏导数中的lanmuda 。。。 这题里化简一下就可以得到 2 li * ki * ​(vi​−vi′​)* vi^2​1 然后一旦li确定 我们会发现这个三次函…

LazyLLM端到端实战:用RAG+Agent实现自动出题与学习计划的个性化学习助手智能体

1. 为什么做这个学习助手Agent? 最近,我在写一本关于Git和开源的技术书,这本书未来有个推广方向,就是面向高校作为教材使用。所以我需要在每一章结束在之后,设计若干道练习题,然后还需要为这本书编写配套的PPT以…

补充图

最小生成树(K算法和P算法)(1)K 算法(每次找最小边,判断是否存在环)TIPS:使用并查集实现合并、查询等操作。(常数级别),这里暂未使用。(2)P 算法

域名+邮件推送+事件总线=实现每天定时邮件!

需求:十二点之前我就要睡觉了,我希望给自己一个提醒,但是这个提醒不能是闹钟,因为我一旦在闹钟之前睡去,这闹钟反而要一直响个不停了。 根据这个需求,我盯上了自动邮件,发现目前市场上的自动邮件服务都是付费居…

llm入门环境

Jupyter Notebook安装 官网 https://jupyter.org/install 命令安装 $ pip install jupyterlab 启动 $ jupyter-lab Langchain安装 命令 $ pip install langchain$ conda inatall langchain -c conda-forge 也可以使用V…