Android开发Jetpack组件Room用例讲解

目录
  • 一、简介
  • 二、导入
  • 三、使用
    • 3.1 创建 Entity 类
    • 3.2 创建 Dao 类
    • 3.3 创建 Database 抽象类
    • 3.4 测试
  • 四、数据库升级
    • 4.1 简单升级
    • 4.2 规范升级
      • 4.2.1 新增一张表
      • 4.2.2 修改一张表
    • 4.3 测试

一、简介

Room 是 Google 官方推出的数据库 ORM 框架。ORM 是指 Object Relational Mapping,即对象关系映射,也就是将关系型数据库映射为面向对象的语言。使用 ORM 框架,我们就可以用面向对象的思想操作关系型数据库,不再需要编写 SQL 语句。

二、导入

apply plugin: 'kotlin-kapt'
dependencies {
    ...
    implementation 'androidx.room:room-runtime:2.2.5'
    kapt 'androidx.room:room-compiler:2.2.5'
}

三、使用

Room 的使用可以分为三步:

创建 Entity 类:也就是实体类,每个实体类都会生成一个对应的表,每个字段都会生成对应的一列。

创建 Dao 类:Dao 是指 Data Access Object,即数据访问对象,通常我们会在这里封装对数据库的增删改查操作,这样的话,逻辑层就不需要和数据库打交道了,只需要使用 Dao 类即可。

创建 Database 类:定义数据库的版本,数据库中包含的表、包含的 Dao 类,以及数据库升级逻辑。

3.1 创建 Entity 类

新建一个 User 类,并添加 @Entity 注解,使 Room 为此类自动创建一个表。在主键上添加 @PrimaryKey(autoGenerate = true) 注解,使得 id 自增,不妨将这里的主键 id 记作固定写法。

@Entity
data class User(var firstName: String, var lastName: String, var age: Int) {
    @PrimaryKey(autoGenerate = true)
    var id: Long = 0
}

3.2 创建 Dao 类

创建一个接口类 UserDao,并在此类上添加 @Dao 注解。增删改查方法分别添加 @Insert@Delete@Update@Query 注解,其中,@Query 需要编写 SQL 语句才能实现查询。Room 会自动为我们生成这些数据库操作方法。

@Dao
interface UserDao {
    @Insert
    fun insertUser(user: User): Long
    @Update
    fun updateUser(newUser: User)
    @Query("select * from user")
    fun loadAllUsers(): List<User>
    @Query("select * from User where age > :age")
    fun loadUsersOlderThan(age: Int): List<User>
    @Delete
    fun deleteUser(user: User)
    @Query("delete from User where lastName = :lastName")
    fun deleteUserByLastName(lastName: String): Int
}

@Query 方法不仅限于查找,还可以编写我们自定义的 SQL 语句,所以可以用它来执行特殊的 SQL 操作,如上例中的 deleteUserByLastName 方法所示。

3.3 创建 Database 抽象类

新建 AppDatabase 类,继承自 RoomDatabase 类,添加 @Database 注解,在其中声明版本号,包含的实体类。并在抽象类中声明获取 Dao 类的抽象方法。

@Database(version = 1, entities = [User::class])
abstract class AppDatabase : RoomDatabase() {
    abstract fun userDao(): UserDao
    companion object {
        private var instance: AppDatabase? = null
        @Synchronized
        fun getDatabase(context: Context): AppDatabase {
            return instance?.let { it }
                ?: Room.databaseBuilder(context.applicationContext, AppDatabase::class.java, "app_database")
                    .build()
                    .apply { instance = this }
        }
    }
}

在 getDatabase 方法中,第一个参数一定要使用 applicationContext,以防止内存泄漏,第三个参数表示数据库的名字。

3.4 测试

布局中只有四个 id 为 btnAdd,btnDelete,btnUpdate,btnQuery 的按钮,故不再给出布局代码。

MainActivity 代码如下:

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        val userDao = AppDatabase.getDatabase(this).userDao()
        val teacher = User("lin", "guo", 66)
        val student = User("alpinist", "wang", 3)
        btnAdd.setOnClickListener {
            thread {
                teacher.id = userDao.insertUser(teacher)
                student.id = userDao.insertUser(student)
            }
        }
        btnDelete.setOnClickListener {
            thread {
                userDao.deleteUser(student)
            }
        }
        btnUpdate.setOnClickListener {
            thread {
                teacher.age = 666
                userDao.updateUser(teacher)
            }
        }
        btnQuery.setOnClickListener {
            thread {
                Log.d("~~~", "${userDao.loadAllUsers()}")
            }
        }
    }
}

每一步操作我们都开启了一个新线程来操作,这是由于数据库操作涉及到 IO,所以不推荐在主线程执行。在开发环境中,我们也可以通过 allowMainThreadQueries() 方法允许主线程操作数据库,但一定不要在正式环境使用此方法。

Room.databaseBuilder(context.applicationContext, AppDatabase::class.java, "app_database")
 .allowMainThreadQueries()
 .build()

点击 btnAdd,再点击 btnQuery,Log 如下:

~~~: [User(firstName=lin, lastName=guo, age=66), User(firstName=alpinist, lastName=wang, age=3)]

点击 btnDelete,再点击 btnQuery,Log 如下:

~~~: [User(firstName=lin, lastName=guo, age=66)]

点击 btnUpdate,再点击 btnQuery,Log 如下:

~~~: [User(firstName=lin, lastName=guo, age=666)]

由此可见,我们的增删改查操作都成功了。

四、数据库升级

4.1 简单升级

使用 fallbackToDestructiveMigration() 可以简单粗暴的升级,也就是直接丢弃旧版本数据库,然后创建最新的数据库

Room.databaseBuilder(context.applicationContext, AppDatabase::class.java, "app_database")
 .fallbackToDestructiveMigration()
 .build()

注:此方法过于暴力,开发阶段可使用,不可在正式环境中使用,因为会导致旧版本数据库丢失。

4.2 规范升级

4.2.1 新增一张表

创建 Entity 类

@Entity
data class Book(var name: String, var pages: Int) {
    @PrimaryKey(autoGenerate = true)
    var id: Long = 0
}

创建 Dao 类

@Dao
interface BookDao {
    @Insert
    fun insertBook(book: Book)
    @Query("select * from Book")
    fun loadAllBooks(): List<Book>
}

修改 Database 类:

@Database(version = 2, entities = [User::class, Book::class])
abstract class AppDatabase : RoomDatabase() {
    abstract fun userDao(): UserDao
    abstract fun bookDao(): BookDao
    companion object {
        private var instance: AppDatabase? = null
        private val MIGRATION_1_2 = object : Migration(1, 2) {
            override fun migrate(database: SupportSQLiteDatabase) {
                database.execSQL(
                    """
                    create table Book (
                    id integer primary key autoincrement not null,
                    name text not null,
                    pages integer not null)
                """.trimIndent()
                )
            }
        }
        @Synchronized
        fun getDatabase(context: Context): AppDatabase {
            return instance?.let { it }
                ?: Room.databaseBuilder(context.applicationContext, AppDatabase::class.java, "app_database")
                    .addMigrations(MIGRATION_1_2)
                    .build()
                    .apply { instance = this }
        }
    }
}

注:这里的修改有:

  • version 升级
  • 将 Book 类添加到 entities 中
  • 新增抽象方法 bookDao
  • 创建 Migration 对象,并将其添加到 getDatabase 的 builder 中

现在如果再操作数据库,就会新增一张 Book 表了。

4.2.2 修改一张表

比如在 Book 中新增 author 字段

@Entity
data class Book(var name: String, var pages: Int, var author: String) {

    @PrimaryKey(autoGenerate = true)
    var id: Long = 0
}

修改 Database,增加版本 2 到 3 的迁移逻辑:

@Database(version = 3, entities = [User::class, Book::class])
abstract class AppDatabase : RoomDatabase() {
    abstract fun userDao(): UserDao
    abstract fun bookDao(): BookDao
    companion object {
        private var instance: AppDatabase? = null
        private val MIGRATION_1_2 = object : Migration(1, 2) {
            override fun migrate(database: SupportSQLiteDatabase) {
                database.execSQL(
                    """
                    create table Book (
                    id integer primary key autoincrement not null,
                    name text not null,
                    pages integer not null)
                """.trimIndent()
                )
            }
        }
        private val MIGRATION_2_3 = object : Migration(2, 3) {
            override fun migrate(database: SupportSQLiteDatabase) {
                database.execSQL(
                    """
                    alter table Book add column author text not null default "unknown"
                """.trimIndent()
                )
            }
        }
        @Synchronized
        fun getDatabase(context: Context): AppDatabase {
            return instance?.let { it }
                ?: Room.databaseBuilder(context.applicationContext, AppDatabase::class.java, "app_database")
                    .addMigrations(MIGRATION_1_2, MIGRATION_2_3)
                    .build()
                    .apply { instance = this }
        }
    }
}

注:这里的修改有:

version 升级创建 Migration 对象,并将其添加到 getDatabase 的 builder 中

4.3 测试

修改 MainActivity:

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        val bookDao = AppDatabase.getDatabase(this).bookDao()
        btnAdd.setOnClickListener {
            thread {
                bookDao.insertBook(Book("第一行代码", 666, "guolin"))
            }
        }
        btnQuery.setOnClickListener {
            thread {
                Log.d("~~~", "${bookDao.loadAllBooks()}")
            }
        }
    }
}

点击 btnAdd,再点击 btnQuery,Log 如下:

~~~: [Book(name=第一行代码, pages=666, author=guolin)]

这就说明我们对数据库的两次升级都成功了。

参考文章

《第一行代码》(第三版)- 第 13 章 13.5 Room

以上就是Android Jetpack组件Room用例讲解的详细内容,更多关于Android Jetpack组件Room的资料请关注我们其它相关文章!

(0)

相关推荐

  • Android操作系统的架构设计分析

    之前一直在Android应用层上做工作,最近开始研究Android平台上的东东了,主要是在Android Frameworks层和系统库层进行研究.以下是我自己的理解,领悟,希望与大家一块分享. Android系统架构分为Linux内核驱动.C/C ++框架.Java框架.Java应用程序. Android应用层: Android应用程序需要Java框架支持.主要是针对手机用户的.Android应用层都是由Java代码写的,运行在虚拟机中.虚拟机在Android平台中扮演着很重要的角色.虚拟机在

  • Android架构组件Room的使用详解

    Room其实就是一个orm,抽象了SQLite的使用,但是它作为Android的亲儿子orm,并且原生支持LiveData和Rxjava嵌套使用,学习一下还是不错的. Room有3个主要组件 Database :数据库 Entity : 代表数据库一个表结构 Dao : 包含访问数据库的方法 简单使用 添加Google Maven仓库 allprojects { repositories { jcenter() google() } } 添加依赖 dependencies { // Room i

  • Android Jetpack中Room的使用

    Room Room主要分三个部分 database.dao和实体类entity Entity entity实体类定义时需要用到@Entity(tableName = "student")注解,其中参数为表名 主键定义需要用到@PrimaryKey(autoGenerate = true)注解,参数决定是否自增长 每个属性(也就是表的字段)都需要加@ColumnInfo(name = "name",typeAffinity = ColumnInfo.TEXT)注解 ,

  • Android Jetpack架构组件Lifecycle详解

    前言 Lifecycle是Jetpack架构组件中用来感知生命周期的组件,使用Lifecycles可以帮助我们写出和生命周期相关更简洁更易维护的代码. 生命周期 生命周期这个简单而又重要的知识相信大家早已耳熟能详.假设我们现在有这样一个简单需求: 这个需求只是一个实例,在真实的开发中当然不可能有这样的需要: 在Activity 可见的时候,我们去做一个计数功能,每隔一秒 将计数加1 ,当Activity不可见的时候停止计数,当Activity被销毁的时候 将计数置为0 OK,So easy~ ,

  • 详解Android JetPack之LiveData的工作原理

    前言 本篇文章主要讲解LiveData工作的原理,如果还不知道LiveData如何用的话,请参考官方文档. LiveData的讲解涉及到了Lifecycle的知识,如果你还不了解LifeCycle,请参考文档LifeCycle介绍. 介绍 LiveData是一个数据持有类,它可以通过添加观察者被其他组件观察其变更.不同于普通的观察者,它最重要的特性就是遵从应用程序的生命周期,如在Activity中如果数据更新了但Activity已经是destroy状态,LivaeData就不会通知Activit

  • Android开发Jetpack组件Room用例讲解

    目录 一.简介 二.导入 三.使用 3.1 创建 Entity 类 3.2 创建 Dao 类 3.3 创建 Database 抽象类 3.4 测试 四.数据库升级 4.1 简单升级 4.2 规范升级 4.2.1 新增一张表 4.2.2 修改一张表 4.3 测试 一.简介 Room 是 Google 官方推出的数据库 ORM 框架.ORM 是指 Object Relational Mapping,即对象关系映射,也就是将关系型数据库映射为面向对象的语言.使用 ORM 框架,我们就可以用面向对象的思

  • Android开发Jetpack组件DataBinding用例详解

    目录 简介 使用方式 1. build.gradle 中添加 kapt,并启用dataBinding 2.修改布局文件,添加 layout 和 data 标签 3.使用 DataBindingUtil 绑定布局 4.布局的 data 标签中添加数据变量,并使用其参数 5.BindingAdapter的使用 简介 DataBinding 是 Jetpack 组件之一,适用于 MVVM 模式开发,也是Google官方推荐使用的组件之一.使用DataBinding可以很容易的达到视图与逻辑分离,直接在

  • Android开发Jetpack组件WorkManager用例详解

    目录 一.简介 二.导入 三.基本使用 3.1 定义后台任务 3.2 配置任务运行条件 3.2.1 只需执行一次的任务 3.2.2 周期性执行的任务 3.3 将任务传给 WorkManager 四.高级配置 4.1 设置任务延迟执行 4.2 给任务添加标签 4.3 取消任务 4.3.1 根据标签取消任务 4.3.2 根据 request 的 id 取消任务 4.3.3 取消所有任务 4.4 任务重试 4.5 监听任务结果 4.6 传递数据 4.7 链式任务 一.简介 WorkManager 用于

  • Android开发Jetpack组件ViewModel使用讲解

    目录 前言 ViewModel概述 ViewModel使用 ViewModel源码 前言 学习ViewModel之前首先我们得简单了解下MVP和MVVM,因为ViewModel是MVVM中的一个元素 MVP MVVM 在MVP中View想要调用Model数据层,需要经过中间层Presenter, 这样就实现了View和Model的解耦,这也是MVP和MVC的差别: 但是如果一个Activity中有太多交互,那么我们的View接口数量就会很庞大达到十几个也不足为奇,并且在View层调用了Prese

  • Android开发Jetpack组件Room使用讲解

    目录 简介 Room使用步骤 1 添加依赖 2 创建Entity实体类 3 声明Dao对象 4 声明Database对象 5 获取数据 6 最终使用 简介 Room 是 Google 官方推出的数据库 ORM 框架.ORM 是指 Object Relational Mapping,即对象关系映射,也就是将关系型数据库映射为面向对象的语言.使用 ORM 框架,我们就可以用面向对象的思想操作关系型数据库,不再需要编写 SQL 语句. Room使用步骤 1 添加依赖 build.gradle {app

  • Android开发Jetpack组件LiveData使用讲解

    目录 LiveData概述 LiveData优势 共享资源 LiveData使用 1 LiveData基本使用 2 Transformations.map() 3 Transformations.switchMap() 4 MediatorLiveData.addSource()合并数据 LiveData概述 LiveData 是一种可观察的数据存储器类: 与常规的可观察类不同,LiveData 具有生命周期感知能力,意指它遵循其他应用组件(如 Activity.Fragment 或 Servi

  • Android开发Jetpack组件Lifecycle原理篇

    目录 前言 1.Lifecycle的生命周期状态事件和状态 2.Lifecycle如何观察Activity和Fragment的生命周期 前言 在上一篇文章中,我们学习了如何去使用Lifecycle: 当然之会使用是不够的,还需要了解它的原理,这是成为优秀工程师必备的:这篇文章就来学习Lifecycle的基本原理 1.Lifecycle的生命周期状态事件和状态 **Lifecycle使用两个枚举来跟踪其关联组件的生命周期状态,这两个枚举分别是Event和State:**State指的是Lifecy

  • Android开发Jetpack组件Lifecycle使用篇

    目录 1.为什么需要Lifecycle 2.如何使用Lifecycle 2.1 依赖Lifecycle库 2.2 Lifecycle基本用法 3.Lifecycle应用举例 3.1 Activity中使用 3.2 MVP中使用 4.自定义LifecycleOwner 1.为什么需要Lifecycle 在应用开发中,处理Activity或者Fragment组件的生命周期相关代码是必不可免的: 官方文档中举了一个例子,这里简化一下,在Activity中写一个监听,在Activity的不同生命周期方法

  • Android开发高级组件之自动完成文本框(AutoCompleteTextView)用法示例【附源码下载】

    本文实例讲述了Android开发高级组件之自动完成文本框(AutoCompleteTextView)用法.分享给大家供大家参考,具体如下: 通常来说自动完成文本框(AutoCompleteTextView)从EditText派生而出,实际上他也是一个编辑框,但他比普通的编辑框多了一个功能:当用户输入一定字符后,自动完成文本框会显示一个下拉菜单,供用户从中选择,当用户选择了某个菜单项过后,AutoCompleteTextView就会按用户选择自动填写该文本框. 自动完成文本框(AutoComple

  • Android开发之组件GridView简单使用方法示例

    本文实例讲述了Android开发之组件GridView简单使用方法.分享给大家供大家参考,具体如下: 案例:简单的图片浏览器,保存图片到相册 保存图片到相册 方法代码:https://www.jb51.net/article/158668.htm 废话不多说先上效果: 具体实现: 首先是布局文件: 1.一个GridView(展示所有的图片) 2.一个ImageView(放选中的图片) <?xml version="1.0" encoding="utf-8"?&

随机推荐