简介

Room 是一个 ORM (Object Relational Mapping)对象关系映射数据库、其底层还是封装的 SQLite 的能力。它赋予了一个强大的功能,就是可以用面向对象的思维来和数据库进行交互,绝大数情况下不用再和SQL语句打交道了,同时也不用担心操作数据库的逻辑会让项目的整体代码变混乱。

Room 主要是由Entity、Dao 和Database 这3部分组成:

  • Entity

用于定义封装实际数据的实体类,每个实体类都会在数据库中有一张对应的表,并且表中的列是根据实体类中的字段自动生成的。

  • Dao

Dao 是数据访问对象的意思,通常会在这里对数据库的各项操作进行封装,在实际编程的时候,逻辑层就不需要和底层数据库打交道了,直接和Dao层进行交互即可。

  • Database

用于定义数据库中的关键信息,包括数据库的版本号、包含哪些实体类以及提供Dao层的访问实例。

依赖

apply plugin: 'kotlin-kapt'dependencies {def roomVersion = "2.2.0"// Roomimplementation("androidx.room:room-runtime:$roomVersion")// java -RoomannotationProcessor("androidx.room:room-compiler:$roomVersion")// kotlin -Roomkapt("androidx.room:room-compiler:$roomVersion")// 支持协程implementation("androidx.room:room-ktx:$roomVersion")
}

具体请看可选依赖

用法

定义 Entity

对于每个实体,系统会在关联的Database对象中创建一个表,以存储这些项。您必须通过 Database 类中的 entities 数组引用实体类。

@Entity(tableName = "book")
data class Book(@PrimaryKey(autoGenerate = true)var id:Int=1,@ColumnInfo(name = "book_name") // 书名var bookName:String,@ColumnInfo(name = "category_name")// 类别var categoryName:String,@ColumnInfo(name = "book_price") // 价格var bookPrice:Int
)
  • @PrimaryKey

每个实体必须将至少1个字段定义为主键。即使只有1个字段,仍然需要为该字段添加@PrimaryKey注释。此外,如果想让Room为实体分配自动 ID,则可以设置@PrimaryKey的autoGenerate 属性。

  • tableName

默认情况下,Room将类名称用作数据库表名称。如果希望表具有不同的名称,请设置 @Entity 注释的 tableName 属性。
注意: SQLite 中的表名称不区分大小写。

  • @ColumnInfo

默认情况下,Room将字段名称用作数据库中的列名称。如果您希望列具有不同的名称,请将 @ColumnInfo 注释添加到字段

DAO(数据访问对象)

DAO 既可以是接口,也可以是抽象类。如果是抽象类,则该 DAO 可以选择有一个以RoomDatabase为唯一参数的构造函数。Room 会在编译时创建每个 DAO 实现。

插入

当您创建 DAO 方法并使用 @Insert 对其进行注释时,Room 会生成一个实现,该实现在单个事务中将所有参数插入数据库中。

@Dao
interface BookDao {@Insert(onConflict = OnConflictStrategy.REPLACE)fun insertBook( book: Book):Long
}// Activity 代码
tv_insert.setOnClickListener {thread {bookDao.insertBook(book)}}

如果 @Insert 方法只接收 1 个参数,则它可以返回 long,这是插入项的新rowId。如果参数是数组或集合,则应返回 long[] 或 List

更新

@Dao
interface BookDao {@Updatefun updateBooks( book: Book)
}// Activity 代码
tv_update.setOnClickListener {thread {bookDao.updateBooks(book)}}

可以让此方法返回一个 int 值,以指示数据库中更新的行数。

删除

Delete 便捷方法会从数据库中删除一组以参数形式给出的实体。它使用主键查找要删除的实体

@Dao
interface BookDao {@Deletefun deleteBooks( book: Book)
}// Activity 代码
tv_delete.setOnClickListener {thread {bookDao.deleteBooks(book)}}

可以让此方法返回一个int值,以指示从数据库中删除的行数。

查询

@Query 是 DAO 类中使用的主要注释。它允许您对数据库执行读/写操作。每个@Query方法都会在编译时进行验证,因此如果查询出现问题,则会发生编译错误,而不是运行时失败

  • 简单查询
@Dao
interface BookDao {@Query("SELECT * FROM book ")fun selectAllBooks(): List<Book>
}//Activity 中代码tv_query.setOnClickListener {thread {val  books=bookDao.selectAllBooks()}}

在编译时,Room 知道它在查询用户表中的所有列。如果查询包含语法错误,或者数据库中没有用户表格,则 Room 会在您的应用编译时显示包含相应消息的错误。

  • 条件查询
    @Query("SELECT * FROM book WHERE category_name IN (:categorys)")fun selectBooksByCategory(categorys: List<String>): List<Book>@Query("SELECT * FROM book WHERE book_price > :price")fun selectBooksByPrice(price: Int): List<Book>
  • 使用 LiveData 进行可观察查询
@Dao
interface BookDao {fun selectBooksByBookName(name: String): LiveData<Book>}// Activity 代码
bookDao.selectBooksByBookName("数据结构与算法").observe(this, { book->tv_show.text="书名${book.bookName}价格:${book.bookPrice}"})

执行查询时,您通常会希望应用的界面在数据发生变化时自动更新。使用 LiveData 类型的返回值。当数据库更新时,Room 会生成更新 LiveData 所必需的所有代码。

  • 使用 Flow 进行可观察查询
    @Query("SELECT * FROM press WHERE press_name = :name")fun selectPressByPressName(name: String): Flow<Press>
  • 使用协程 进行查询
    @Query("SELECT * FROM press WHERE press_name = :name")suspend fun selectPressByPressName(name: String): Press// Activity 中代码lifecycle.coroutineScope.launch{val press=pressDao.selectPressByPressName("电子出版社")}

selectPressByPressName() 是一个挂起函数,挂起函数必须在协程中或者挂起函数中使用。

注意: 除非已对构建器调用allowMainThreadQueries()(正式开发环境不建议使用此方法),否则 Room 不支持在主线程上访问数据库,因为它可能会长时间锁定界面。异步查询(返回 LiveData 或 Flowable 实例的查询)无需遵守此规则,因为此类查询会根据需要在后台线程上异步运行查询。

数据库 Database

@Database(entities = [Book::class], version = 1)
abstract class AppDatabase : RoomDatabase() {abstract fun getBookDao(): BookDaocompanion object {private var instance: AppDatabase? = nullfun getDatabase(mContext: Context): AppDatabase {instance?.let {return it}return Room.databaseBuilder(mContext.applicationContext,AppDatabase::class.java, "app_database").build().apply {instance = thisinstance}}}
}

注意: 如果您的应用在单个进程中运行,在实例化 AppDatabase 对象时应遵循单例设计模式。每个 RoomDatabase 实例的成本相当高,而您几乎不需要在单个进程中访问多个实例。

如果您的应用在多个进程中运行,请在数据库构建器调用中包含 enableMultiInstanceInvalidation()。这样,如果您在每个进程中都有一个AppDatabase实例,可以在一个进程中使共享数据库文件失效,并且这种失效会自动传播到其他进程中 AppDatabase 的实例。

数据库升级

现有表增加新列

  • Book 数据类增加了bookAuthor(作者)属性
@Entity(tableName = "book")
data class Book(@PrimaryKey(autoGenerate = true)var id:Int=1,@ColumnInfo(name = "book_name") // 书名var bookName:String,@ColumnInfo(name = "category_name")// 类别var categoryName:String,@ColumnInfo(name = "book_price") // 价格var bookPrice:Int,@ColumnInfo(name = "book_author") // 作者var bookAuthor:String
)
  • 修改AppDatabase代码,升级数据库中表
@Database(entities = [Book::class], version = 2)
abstract class AppDatabase : RoomDatabase() {abstract fun getBookDao(): BookDaocompanion object {private var instance: AppDatabase? = null// 实现Migration 的匿名类val MIGRATION_1_2=object :Migration(1,2){override fun migrate(database: SupportSQLiteDatabase) {database.execSQL("alter table book add column book_author text not null default 'unknown' ")}}fun getDatabase(mContext: Context): AppDatabase {instance?.let {return it}return Room.databaseBuilder(mContext.applicationContext,AppDatabase::class.java, "app_database.db")// 升级表.addMigrations(MIGRATION_1_2).build().apply {instance = thisinstance}}}
}

在@Database 注解中,将版本号升级成了2,在companion object结构体中实现了Migration的匿名类。

增加新表

  • 新增一个数据类Press
@Entity(tableName = "press")
data class Press(@PrimaryKeyvar id:Int,@ColumnInfo(name = "press_name") // 名字var name:String ,@ColumnInfo(name = "press_address") // 地址var address:String)
  • 创建新表
@Database(entities = [Book::class,Press::class], version = 3)
abstract class AppDatabase : RoomDatabase() {abstract fun getBookDao(): BookDaoabstract fun getPressDao(): PressDaocompanion object {private var instance: AppDatabase? = nullval MIGRATION_1_2=object :Migration(1,2){override fun migrate(database: SupportSQLiteDatabase) {database.execSQL("alter table book add column book_author text not null default 'unknown' ")}}val MIGRATION_2_3=object :Migration(2,3){override fun migrate(database: SupportSQLiteDatabase) {database.execSQL("create table press(id integer primary key not null,press_name text not null,press_address text not null)")}}fun getDatabase(mContext: Context): AppDatabase {instance?.let {return it}return Room.databaseBuilder(mContext.applicationContext,AppDatabase::class.java, "app_database.db").addMigrations(MIGRATION_1_2, MIGRATION_2_3).build().apply {instance = thisinstance}}}
}

数据库查看数据

  • 导出数据库文件app_database.db

打开Device File Explorer,打开目录 data/data/包名/databases/ app_database.db,如图所示:

保存app_database.db 到指定位置:

  • 安装 DB Navigator 查看数据

在AndroidStudio中 settings->plugins 下载插件DB Navigator。

安装重启后,会出现如图所示:

连接数据库,如下图所示:

打开导出数据库文件,如下图所示:

查看数据,如下图所示:

参考资料

  • Room 官方文档
  • Android ROOM 数据库高手秘籍
  • Android从零开始搭建MVVM架构(4)————Room(从入门到进阶)

Jetpack 架构组件:Room 数据库应用相关推荐

  1. Android Jetpack架构组件之 Room(使用、源码篇)

    2019独角兽企业重金招聘Python工程师标准>>> 1.前言 最近简单看了下google推出的框架Jetpack,感觉此框架的内容可以对平时的开发有很大的帮助,也可以解决很多开发 ...

  2. android 使用4大组件的源码,Android Jetpack架构组件之 Paging(使用、源码篇)

    1.前言 最近简单看了下google推出的框架Jetpack,感觉此框架的内容可以对平时的开发有很大的帮助,也可以解决很多开发中的问题,对代码的逻辑和UI界面实现深层解耦,打造数据驱动型UI界面. A ...

  3. ios navigation的返回按钮长按_Android Jetpack架构组件 — Navigation入坑详解 [转]

    前言 这是最近看见的觉得比较有意思的文,希望对大家的学习有帮助. Navigation 直接翻译即为导航,它是 Android Jetpack 组件之一,让单 Activity 应用成为首选架构.应用 ...

  4. Android Jetpack架构组件(一)带你了解Android Jetpack

    本文首发于微信公众号「后厂村码农」 前言 Android已经发展了11年,可以说是比较成熟的技术了,一开始时框架很少,也没有什么规范,所有的代码都是要自己写,比如网络请求,数据库请求,数据解析等等.后 ...

  5. Jetpack架构组件 (一)-- Android Jetpack 简介

    前言 Android 已经发展十多年了,可以说是比较成熟的技术了,一开始时框架很少,也没有什么规范,所有的代码都是要自己写,比如网络请求,数据库操作,数据解析等等.后来出现了一些框架来帮助开发者快速进 ...

  6. Retrofit+kotlin Coroutines(协程)+mvvm(Jetpack架构组件)实现更简洁的网络请求

    前言 使用kotlin协程也有一段时间了,给我最大的感受就是完全可以替代Rxjava了,并且写起来更加的简洁. 6月份Retrofit发布的2.6.0版本内部支持了kotlin协程中的挂起(suspe ...

  7. android置组件下面,Android Jetpack架构组件(十二)之Hilt

    一. 依赖注入简介 依赖注入(英文Dependency Injection,简写DI)是一种被广泛使用的编程技术,主要的作用代码解耦. 借助依赖注入,我们可以轻松的管理类之间的依赖,并最终建立高可维护 ...

  8. Android Jetpack架构组件之Navigation

    Navigation 导航是指支持用户导航.进入和退出应用中不同内容片段的交互.Android Jetpack 的导航组件可帮助您实现导航,无论是简单的按钮点击,还是应用栏和抽屉式导航栏等更为复杂的模 ...

  9. Android Jetpack 架构组件之 Room

    一.Room简介 Room 持久性库在 SQLite 的基础上提供了一个抽象层,让用户能够在充分利用 SQLite 的强大功能的同时,获享更强健的数据库访问机制.该库可帮助您在运行应用的设备上创建应用 ...

最新文章

  1. 通俗易懂----尾递归
  2. (转)python协程2:yield from 从入门到精通
  3. mac安装python3
  4. Drupal的高速缓存配置APC
  5. 实现Unity编辑器模式下的旋转
  6. Android多媒体开发:照相机
  7. 最近工程师怎么都在写公众号?这玩意到底能赚多少钱?
  8. qt之qml开发优缺点_linux配置vlc-qt
  9. C++happy number开心数的实现算法(附完整源码)
  10. Android 添加 *.arr
  11. CVPR2015深度学习回顾
  12. 微信公众平台开发教程第21篇-“可信网址”白名单
  13. 9 CO配置-控制-成本中心会计-在成本控制范围激活成本中心会计核算
  14. 【springboot 踩坑记录】拦截器中无法注入bean
  15. unity三维地图的经纬度如何在二维地图上表示_安全数据分析:数据点—地图—线性回归...
  16. 同事乱用分页 MySQL 卡爆,我真是醉了...
  17. Android APP压力测试 之Monkey日志自动分析脚本
  18. C#控件编程详解之文本框(TextBox)
  19. 慢慢说计网:HTTP 长短连接
  20. 一款科幻题材基地建设策略游戏——太空避难所中文版 附游戏玩法

热门文章

  1. Thymeleaf从后端取数据到js中 js中字符串转数字的函数应用
  2. js查找数组元素位置
  3. SparkSQL的入门实践教程
  4. 一个中东外贸业务员分享的干货
  5. Qt 5.12.10 用Design ui 设计界面,连续输入中文时,变成英文输入
  6. 嘉兴 机器人仓库 菜鸟_揭秘菜鸟自动化仓库 机器人物联网+大数据(附视频)...
  7. persistence.cpp:2197:ann.xml(1): There should be space between attributes in function icvXMLParseTag
  8. 从Darry Ring看奢侈品该如何玩转互联网思维
  9. linux下安装录制视频软件Simple Screen Recorder
  10. python数据结构与算法 20 递归和递归三定律