IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> Jetpack 架构组件:Room 数据库应用 -> 正文阅读

[移动开发]Jetpack 架构组件:Room 数据库应用

简介

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"
    // Room
    implementation("androidx.room:room-runtime:$roomVersion")
    // java -Room
    annotationProcessor("androidx.room:room-compiler:$roomVersion")
    // kotlin -Room
    kapt("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 {
    @Update
    fun updateBooks( book: Book)
}

// Activity 代码
tv_update.setOnClickListener {
            thread {
                bookDao.updateBooks(book)
            }
    
}

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

删除

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

@Dao
interface BookDao {
    @Delete
    fun 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(): BookDao

    companion object {
        private var instance: AppDatabase? = null

        fun getDatabase(mContext: Context): AppDatabase {
            instance?.let {
                return it
            }

            return Room.databaseBuilder(mContext.applicationContext,
                    AppDatabase::class.java, "app_database")
                    .build().apply {
                        instance = this

                        instance
                    }
        }
    }
}

注意: 如果您的应用在单个进程中运行,在实例化 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(): BookDao

    companion 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 = this

                        instance
                    }
        }

    }
}

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

增加新表

  • 新增一个数据类Press
@Entity(tableName = "press")
data class Press(
        @PrimaryKey
        var 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(): BookDao

    abstract fun getPressDao(): PressDao

    companion object {
        private var instance: AppDatabase? = null

        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' ")
            }

        }

        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 = this

                        instance
                    }
        }
    }
}

数据库查看数据

  • 导出数据库文件app_database.db

打开Device File Explorer,打开目录 data/data/包名/databases/ app_database.db,如图所示:
在这里插入图片描述
保存app_database.db 到指定位置:
在这里插入图片描述

  • 安装 DB Navigator 查看数据

在AndroidStudio中 settings->plugins 下载插件DB Navigator。
在这里插入图片描述
安装重启后,会出现如图所示:
在这里插入图片描述
连接数据库,如下图所示:
在这里插入图片描述
打开导出数据库文件,如下图所示:
在这里插入图片描述
查看数据,如下图所示:在这里插入图片描述
在这里插入图片描述

参考资料

  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2022-01-17 11:36:04  更:2022-01-17 11:36:13 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 11:45:19-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码