Jetpack 总结系列- Google-Room数据库使用

Posted ZhangQiang-

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Jetpack 总结系列- Google-Room数据库使用相关的知识,希望对你有一定的参考价值。

官方文档 : Room  谷歌实验室:官方教程 SQL语法:SQLite教程

与greendao的区别:同样基于ORM模式封装的数据库。而Room和其他ORM对比,具有编译时验证查询语句正常性,支持LiveData数据返回等优势。

第一步 添加依赖

apply plugin: 'kotlin-kapt'
dependencies 
    // Room
    implementation "androidx.room:room-runtime:2.2.5"
    kapt "androidx.room:room-compiler:2.2.5"
    androidTestImplementation "androidx.room:room-testing:$rootProject.roomVersion"
//注意:对于基于 Kotlin 的应用,请确保使用 kapt,而不是 annotationProcessor。您还应添加 kotlin-kapt 插件。

//注意:对于基于 Kotlin 的应用,请确保使用 kapt,而不是 annotationProcessor。您还应添加 kotlin-kapt 插件。

配置编译器选项

defaultConfig 
    //指定room.schemaLocation生成的文件路径  处理Room 警告 Schema export Error
    javaCompileOptions 
        annotationProcessorOptions 
            arguments = [
                    //配置并启用将数据库架构导出到给定目录中的 JSON 文件的功能。如需了解详情,请参阅 Room 迁移。
                    "room.schemaLocation"  : "$projectDir/schemas".toString(),
                    "room.incremental"     : "true",//启用 Gradle 增量注释处理器
                    "room.expandProjection": "true"]//配置 Room 以重写查询
        
    

第二步 创建表(实体)

注解

说明

@Entity

声明这是一个表(实体),主要参数:tableName-表名、foreignKeys-外键、indices-索引。

@ColumnInfo

主要用来修改在数据库中的字段名。 @ColumnInfo(name = "shoe_id")

@PrimaryKey

声明该字段主键并可以声明是否自动创建。   @PrimaryKey(autoGenerate = true)

@Ignore

声明某个字段只是临时用,不存储在数据库中。

@Embedded

用于嵌套,里面的字段同样会存储在数据库中。

@ForeignKey 

 

外键约束

 @Entity(tableName = "xxx")

 @Entity(tableName = "xxx")
    data class User(
        @PrimaryKey val uid: Int,
        @ColumnInfo(name = "first_name") val firstName: String?,
        @ColumnInfo(name = "last_name") val lastName: String?
    )
    
/** 示例
* 鞋表
*/@Entity(tableName = "shoe")data class Shoe(
    @ColumnInfo(name = "shoe_name") val name: String // 鞋名
    , @ColumnInfo(name = "shoe_description") val description: String// 描述
    , @ColumnInfo(name = "shoe_price") val price: Float // 价格
    , @ColumnInfo(name = "shoe_brand") val brand: String // 品牌
    , @ColumnInfo(name = "shoe_imgUrl") val imageUrl: String // 图片地址) 
    @PrimaryKey(autoGenerate = true)
    @ColumnInfo(name = "id")
    var id: Long = 0

 

第三步 创建Dao

   @Dao
    interface UserDao 
        @Query("SELECT * FROM user")
        fun getAll(): List<User>

        @Query("SELECT * FROM user WHERE uid IN (:userIds)")
        fun loadAllByIds(userIds: IntArray): List<User>

        @Query("SELECT * FROM user 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)


        @Delete
        fun delete(user: User)
    

示例:

增 @Insert注解声明当前的方法为新增的方法,并且可以设置当新增冲突的时候处理的方法。

@Insert(onConflict = OnConflictStrategy.REPLACE)主键相同的话,旧数据会替换新数据。

@Daointerface ShoeDao

    // 省略...

    // 增加一双鞋子

    @Insert(onConflict = OnConflictStrategy.REPLACE)

    fun insertShoe(shoe: Shoe)

    // 增加多双鞋子

    // 除了List之外,也可以使用数组

    @Insert(onConflict = OnConflictStrategy.REPLACE)

    fun insertShoes(shoes: List<Shoe>)

查  Room的查很接近原生的SQL语句。@Query注解不仅可以声明这是一个查询语句,也可以用来删除和修改,不可以用来新增。

 

简单查询  查询多个的时候,可以返回List和数组,还可以配合LiveData和RxJava 2。

@Daointerface ShoeDao

    // 查询一个

    @Query("SELECT * FROM shoe WHERE id=:id")

    fun findShoeById(id: Long): Shoe?

    // 查询多个 通过品牌查询多款鞋

    @Query("SELECT * FROM shoe WHERE shoe_brand=:brand")

    fun findShoesByBrand(brand: String): List<Shoe>

    // 模糊查询 排序 同名鞋名查询鞋

    @Query("SELECT * FROM shoe WHERE shoe_name LIKE :name ORDER BY shoe_brand ASC")

    fun findShoesByName(name:String):List<Shoe>

    // 配合LiveData 返回所有的鞋子

    @Query("SELECT * FROM shoe")

    fun getAllShoesLD(): LiveData<List<Shoe>>

    // 配合LiveData 通过Id查询单款鞋子

    @Query("SELECT * FROM shoe WHERE id=:id")

    fun findShoeByIdLD(id: Long): LiveData<Shoe>

    // 配合RxJava 通过Id查询单款鞋子

    @Query("SELECT * FROM shoe WHERE id=:id")

    fun findShoeByIdRx(id: Long): Flowable<Shoe>

    // 省略...

 

复合查询

@Daointerface ShoeDao

    // 省略...

    // 根据收藏结合 查询用户喜欢的鞋的集合 内联查询

    @Query(

        "SELECT shoe.id,shoe.shoe_name,shoe.shoe_description,shoe.shoe_price,shoe.shoe_brand,shoe.shoe_imgUrl " +

                "FROM shoe " +

                "INNER JOIN fav_shoe ON fav_shoe.shoe_id = shoe.id " +

                "WHERE fav_shoe.user_id = :userId"

    )

    fun findShoesByUserId(userId: Long): LiveData<List<Shoe>>

 

删  @Delete注解声明当前的方法是一个删除方法。

@Daointerface ShoeDao

    // 省略...

    // 删除一双鞋子

    @Delete

    fun deleteShoe(shoe: Shoe)

    // 删除多个鞋子

    // 参数也可以使用数组

    @Delete

    fun deleteShoes(shoes:List<Shoe>)

改  @Update注解声明当前方法是一个更新方法

@Daointerface ShoeDao

    // 省略...

    // 更新一双鞋

    @Update

    fun updateShoe(shoe:Shoe)

    // 更新多双鞋

    // 参数也可以是集合

    @Update

    fun updateShoes(shoes:Array<Shoe>)

 

更新部分字段:

@Query("UPDATE people SET is_alive= :alive WHERE name = :name")

public abstract int setIsAliveByName(String name, int alive);

第四步 创建数据库

/**
* 数据库文件
*/@Database(entities = [User::class,Shoe::class,FavouriteShoe::class],version = 1,exportSchema = false)abstract class AppDataBase:RoomDatabase() 
    // 得到UserDao
    abstract fun userDao():UserDao
    // 得到ShoeDao
    abstract fun shoeDao():ShoeDao
    // 得到FavouriteShoeDao
    abstract fun favouriteShoeDao():FavouriteShoeDao
    companion object
        @Volatile
        private var instance:AppDataBase? = null


        fun getInstance(context:Context):AppDataBase
            return instance?: synchronized(this)
                instance?:buildDataBase(context)
                    .also 
                        instance = it
                    
            
        
        private fun buildDataBase(context: Context):AppDataBase
            return Room
                .databaseBuilder(context,AppDataBase::class.java,"jetPackDemo-database")
                .addCallback(object :RoomDatabase.Callback()
                    override fun onCreate(db: SupportSQLiteDatabase) 
                        super.onCreate(db)


                        // 读取鞋的集合
                        val request = OneTimeWorkRequestBuilder<ShoeWorker>().build()
                        WorkManager.getInstance(context).enqueue(request)
                    
                )
                .build()
        
    
@Database注解声明当前是一个数据库文件,注解中entities变量声明数据库中的表(实体),以及其他的例如版本等变量。同时,获取的Dao也必须在数据库类中。完成之后,点击build目录下的make project,系统就会自动帮我创建AppDataBase和xxxDao的实现类。

第五步 简要封装

在不使用LiveData和RxJava的前提下,Room的操作是不可以放在主线程中的。
/**
* 用户处理仓库
*/class UserRepository private constructor(private val userDao: UserDao) 
    //...
    /**
     * 登录用户 本地数据库的查询
     */
    fun login(account: String, pwd: String):LiveData<User?>
            = userDao.login(account,pwd)
    /**
     * 注册一个用户 本地数据库的新增
     */
    suspend fun register(email: String, account: String, pwd: String):Long 
        return withContext(IO) 
             userDao.insertUser(User(account, pwd, email))
        
    
    companion object 
        @Volatile
        private var instance: UserRepository? = null
        fun getInstance(userDao: UserDao): UserRepository =
            // ...
    
register()方法是一个普通方法,所以它需要在子线程使用,如代码所见,通过协程实现。login()是配合LiveData使用的,不需要额外创建子线程,但是他的核心数据库操作还是在子线程中实现的。

 

 

 


参考:

Google文档    ;   Room库版本   ;  SQLite教程0-菜鸟

Android从零开始搭建MVVM架构(4)————Room(从入门到进阶)

即学即用Android Jetpack - Room

Jetpack架构组件 — Room入坑详解

快速掌握Room数据库框架

一篇好文之Android数据库 GreenDao的完全解析

Room数据库迁移     ;    Room数据库升级(知乎)     ;    Room数据库版本升级,数据迁移详解     ;   Room数据库的版本升级姿势

 

以上是关于Jetpack 总结系列- Google-Room数据库使用的主要内容,如果未能解决你的问题,请参考以下文章

Android Jetpack系列之MVI架构

Jetpack从入门到几乎入门

Jetpack Compose - animateXxxAsState

Jetpack系列 — Navigation

Kotlin基础从入门到进阶系列讲解(进阶篇)Jetpack,(更新中)

Jetpack系列 — LiveData