Android room数据库使用最佳入门教程

3,448 阅读5分钟

引言

  • 本文章旨在记录学习Android JetPack库下的Room数据库的使用,不作过于深入原理的讲解,如有问题和建议请留言讨论。

1、Room 库概念及架构示意图

概念

Google的介绍:
  处理大量结构化数据的应用可极大地受益于在本地保留这些数据。最常见的使用场景是缓存相关的数据,这样一来,当设备无法访问网络时,用户仍然可以在离线状态下浏览该内容,   Room 持久性库在 SQLite 上提供了一个抽象层,以便在充分利用 SQLite 的强大功能的同时,能够流畅地访问数据库

Room库优势:

  • 针对 SQL 查询的编译时验证
  • 可最大限度减少重复和容易出错的样板代码的方便注解
  • 简化了数据库迁移路径

架构示意图

在这里插入图片描述

主要组件

Room 包含三个主要组件

  • 数据库类(Room Database),用于保存数据库并作为应用持久性数据底层连接的主要访问点
  • 数据实体(Entities),用于表示应用的数据库中的表
  • 数据访问对象 (Data Access Objects【Dao】),提供您的应用可用于查询、更新、插入和删除数据库中的数据的方法

2、使用(Kotlin版【带注释】)

效果图展示

room.gif

附上代码库地址:github.com/zqf-dev/ZRo…

依赖项添加

  • 在build.gradle(Project)文件下添加版本
ext {
    roomVersion = '2.4.2'
}
  • 在build.gradle(app)文件下添加
plugins {
	id 'com.android.application'
	id 'kotlin-android'
	// 添加kapt注解处理器
	id 'kotlin-kapt'
}

android {
	//排除原子函数模块并防止出现警告
	packagingOptions {
		exclude 'META-INF/atomicfu.kotlin_module'
	}
	kotlinOptions {
        jvmTarget = "1.8"
    }
}

dependencies{
	//ktx
	implementation 'androidx.core:core-ktx:1.7.0'
	// room
	implementation "androidx.room:room-ktx:$rootProject.roomVersion"
	kapt "androidx.room:room-compiler:$rootProject.roomVersion"
	// 展示数据 adapter
    implementation 'com.github.CymChad:BaseRecyclerViewAdapterHelper:3.0.7'
}

实体类(Entities)创建

在这里插入图片描述

数据库(Database)创建

*注:exportSchema:是否允许数据库架构将导出到给定的文件夹中【 默认true 】

/**
 * Author: zqf
 * Date: 2022/05/11
 * 数据库创建
 * entities: 实体类
 * version: 数据库初始版本号
 * exportSchema: 是否允许数据库架构将导出到给定的文件夹中【 默认true 】
 *
 */
@Database(entities = [User::class], version = 1, exportSchema = false)
abstract class ZRoomDB : RoomDatabase() {

    //创建userDao
    abstract fun userDao(): UserDao
}

数据访问对象 (DAO)创建

/**
 * Author: zqf
 * Date: 2022/05/11
 * 数据访问对象
 */
@Dao
interface UserDao {
    // 查询
    @Query("SELECT * FROM loginUser")
    fun queryAllUser(): MutableList<User>

    //根据姓名参数查询
    @Query("SELECT * FROM loginUser WHERE name = :name")
    fun queryFindUser(name: String): User?

    // 添加单条数据
    @Insert
    fun addUser(vararg user: User)

    // 添加批量数据
    @Insert(onConflict = OnConflictStrategy.REPLACE)
    fun addBatchUser(list: MutableList<User>)

    // 更新某一个数据
    @Update
    fun updateUser(vararg user: User)

    //更新所有数据
    @Query("UPDATE loginUser set age='50'")
    fun updateAll()

    //删除某一个数据
    @Delete
    fun deleteSingle(vararg user: User)

    //删除表里所有数据
    @Query("DELETE FROM loginUser")
    fun deleteAllUser()
}

数据管理类封装

/**
 * Author: zqf
 * Date: 2022/05/11
 * 数据库管理工具
 */
object DbManager {

    //数据库名
    private const val dbName: String = "zroom"

    //懒加载创建数据库
    val db: ZRoomDB by lazy {
        Room.databaseBuilder(
            App.app.applicationContext, ZRoomDB::class.java, dbName
        ).allowMainThreadQueries()//允许在主线程操作
            .addCallback(DbCreateCallBack)//增加回调监听
            .addMigrations()//增加数据库迁移
            .build()
    }

    private object DbCreateCallBack : RoomDatabase.Callback() {
        //第一次创建数据库时调用
        override fun onCreate(db: SupportSQLiteDatabase) {
            super.onCreate(db)
            Log.e("TAG", "first onCreate db version: " + db.version)
        }
    }
}

使用方法

class MainActivity : AppCompatActivity() {
    private val TagL = MainActivity::class.java.simpleName
    private lateinit var queryBtn: Button
    private lateinit var insertBtn: Button
    private lateinit var updateBtn: Button
    private lateinit var deleteBtn: Button
    private lateinit var resultRecycle: RecyclerView
    private var list = mutableListOf<User>()
    var userDao: UserDao = DbManager.db.userDao()

    private val userAdapter by lazy {
        UserAdapter(R.layout.user_item_layout)
    }

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

    private fun findVbId() {
        queryBtn = findViewById(R.id.query)
        insertBtn = findViewById(R.id.insert)
        updateBtn = findViewById(R.id.update)
        deleteBtn = findViewById(R.id.delete)
        resultRecycle = findViewById(R.id.result_recycle)
        resultRecycle.layoutManager = LinearLayoutManager(this, RecyclerView.VERTICAL, false)
        resultRecycle.adapter = userAdapter
        userAdapter.addChildClickViewIds(R.id.item_delete, R.id.item_modify)
        queryBtn.setOnClickListener { query() }
        insertBtn.setOnClickListener { insertSingle() }
        deleteBtn.setOnClickListener { delete() }
        updateBtn.setOnClickListener { update() }
        userAdapter.setOnItemChildClickListener { _, view, position ->
            when (view.id) {
                R.id.item_delete -> singleDel(list[position])
                R.id.item_modify -> singleModify(list[position])
            }
        }
        insertAll()
    }

    /**
     * 查询
     */
    private fun query() {
        list = userDao.queryAllUser()
        Log.e(TagL, list.toString())
        userAdapter.setList(list)
    }

    /**
     * 默认插入批量数据
     */
    private fun insertAll() {
        runBlocking {
            if (userDao.queryAllUser().size == 0) {
                val mutableList: MutableList<User> = mutableListOf()
                for (a in 1..3) {
                    val user = User("张三$a", 20 + a, "贵阳市观山湖区$a", "")
                    mutableList.add(user)
                }
                userDao.addBatchUser(mutableList)
                ToastUtil.show("批量新增数据成功")
            }
            query()
        }
    }

    /**
     * 插入单条数据
     */
    private fun insertSingle() {
        val age = Random.nextInt(20, 40)
        val user = User("小二", age, "贵阳市观山湖区", "")
        userDao.addUser(user)
        ToastUtil.show("新增一条数据成功")
        query()
    }

    /**
     * 删除表里的所有数据
     */
    private fun delete() {
        userDao.deleteAllUser()
        ToastUtil.show("删除所有数据成功")
        query()
    }

    /**
     * 更新所有数据
     */
    private fun update() {
        userDao.updateAll()
        ToastUtil.show("更新表里所有年龄数据成功")
        query()
    }

    /**
     * 删除loginUser表里的指定删除某一个
     */
    private fun singleDel(singleUser: User) {
        userDao.deleteSingle(singleUser)
        ToastUtil.show("删除单条数据成功")
        query()
    }

    /**
     * 修改数据表里某一个对象的字段值
     */
    private fun singleModify(user: User) {
        user.aliasName = "修改的" + user.aliasName
        user.age = 100
        user.ads = "修改的地址白云区"
        userDao.updateUser(user)
        ToastUtil.show("更新单条数据成功")
        list.clear()
        query()
    }
}

数据库升级

  • Room 同时支持自动和手动方式进行增量迁移
  • *注:Room 在 2.4.0-alpha01 及更高版本中支持自动迁移。如果您的应用使用的是较低版本的 Room,则必须手动定义迁移。

1、手动迁移

  • 在实体类User里新增 字段gender
/**
 * Author: zqf
 * Date: 2022/05/11
 * 数据实体
 * TODO
 *  1、@Entity:Room实体类注解 ---> 默认情况下,Room 将【类名称】用作数据库【表名称】
 *      注:如果您希望【数据库表】具有不同的名称,请设置 @Entity 注解的 tableName 属性
 *  2、PrimaryKey: 主键( autoGenerate = true 主键自增 ),作用:唯一标识相应数据库表中的每一行
 *  3、@ColumnInfo:数据库表字段---> 默认情况下,使用【字段名称】作为数据库中的【列名称】
 *      注:如果您希望【数据表里的列】具有不同的名称,请将 @ColumnInfo 注解添加到该字段并设置 name 属性
 *  4、Ignore: 默认情况下,Room 会为实体中定义的每个字段创建一个列。 如果您不想保留该字段,则可以使用 @Ignore 为字段添加注解
 */
@Entity(tableName = "loginUser")
data class User(
    @PrimaryKey(autoGenerate = true)
    var id: Int = 0,
    @ColumnInfo(name = "name")
    var aliasName: String = "",
    var age: Int = 0,
    var ads: String = "",
    @Ignore
    var avatar: String = "",
    //数据库升级时测试的 新增字段
    var gender: Int = 0
) {
    constructor(aliasName: String, age: Int, ads: String, avatar: String) : this() {
        this.aliasName = aliasName
        this.age = age
        this.ads = ads
        this.avatar = avatar
    }
}
  • 1)创建 Migration 类,版本从v1--->v2,添加了‘gender’字段 默认值为1
  • 2)SQL语句:   2.1)数据库表里增加字段    ALTER TABLE loginUser ADD gender INTEGER Default 1 not null   2.2)数据库新增表    CREATE TABLE Car (id INTEGER, name TEXT, PRIMARY KEY(id))
/**
     * 数据库升级
     */
    private object ZMigration : Migration(1, 2) {
        override fun migrate(database: SupportSQLiteDatabase) {
            Log.e(TagL, "执行数据库升级: ")
            //loginUser表中增加字段gender
            database.execSQL("ALTER TABLE loginUser ADD gender INTEGER Default 1 not null")
            //新建汽车数据表
            //database.exceSQL("CREATE TABLE Car (id INTEGER, name TEXT, PRIMARY KEY(id))")
        }
    }
//懒加载创建数据库
//懒加载创建数据库
    val db: ZRoomDB by lazy {
        Room.databaseBuilder(
            App.app.applicationContext, ZRoomDB::class.java, dbName
        ).allowMainThreadQueries()//允许在主线程操作
            .addCallback(DbCreateCallBack)//增加回调监听
            .addMigrations(ZMigration)//增加数据库迁移
            .build()
    }
  • 修改Database version版本为 2,依次调整
@Database(entities = [User::class], version = 2, exportSchema = false)
abstract class ZRoomDB : RoomDatabase() {

    //创建userDao
    abstract fun userDao(): UserDao
}

执行结果: 在这里插入图片描述