您当前的位置: 首页 >  android jetpack

Kevin-Dev

暂无认证

  • 1浏览

    0关注

    544博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

【Android Jetpack 学习之旅】--> Room 的使用

Kevin-Dev 发布时间:2022-04-30 08:15:00 ,浏览量:1

不断学习,做更好的自己!💪

视频号CSDN简书欢迎打开微信,关注我的视频号:KevinDev点我点我

我们在日常的工作中,免不了和数据打交道,因此,存储数据便是一项很重要的工作,在此之前,我使用过 GreenDao、DBFlow 等优秀的 ORM 数据库框架,但是,这些框架都不是谷歌官方的,现在,我们有了谷歌官方的 Room 数据库框架,看看它能够给我们带来什么?

介绍

官网介绍: The Room persistence library provides an abstraction layer over SQLite to allow for more robust database access while harnessing the full power of SQLite.

简单来说:Room 是一个基于 SQLite 的强大数据库框架。

优点:

  • 使用编译时注解,能够对 @Query@Entity 里面的 SQL 语句等进行验证。
  • SQL 语句的使用更加贴近,能够降低学习成本。
  • RxJava 2的支持(大部分都Android数据库框架都支持),对LiveData的支持。
  • @Embedded 能够减少表的创建。

学习资料:

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

1. 在 app/build.gradle 添加:

apply plugin: 'kotlin-kapt'
dependencies {
    // room
    implementation "androidx.room:room-runtime:$rootProject.roomVersion"
    implementation "androidx.room:room-ktx:$rootProject.roomVersion"
    kapt "androidx.room:room-compiler:$rootProject.roomVersion"
    androidTestImplementation "androidx.room:room-testing:$rootProject.roomVersion"
}

项目下的 build.gradle 添加:

ext {
   roomVersion = '2.1.0-alpha06'
}

2. 创建表(实体)

  • 用户表:
/**
 * 用户表
 */
@Entity(tableName = "user")
data class User(
    @ColumnInfo(name = "user_account") val account: String // 账号
    , @ColumnInfo(name = "user_pwd") val pwd: String // 密码
    , @ColumnInfo(name = "user_name") val name: String
    , @Embedded val address: Address // 地址
    , @Ignore val state: Int // 状态只是临时用,所以不需要存储在数据库中
) {
    @PrimaryKey(autoGenerate = true)
    @ColumnInfo(name = "id")
    var id: Long = 0
}
  • 收藏记录表:
/**
 * 喜欢的球鞋
 */
@Entity(
    tableName = "fav_shoe"
    , foreignKeys = [ForeignKey(entity = Shoe::class, parentColumns = ["id"], childColumns = ["shoe_id"])
        , ForeignKey(entity = User::class, parentColumns = ["id"], childColumns = ["user_id"])
    ],indices = [Index("shoe_id")]
)
data class FavouriteShoe(
    @ColumnInfo(name = "shoe_id") val shoeId: Long // 外键 鞋子的id
    , @ColumnInfo(name = "user_id") val userId: Long // 外键 用户的id
    , @ColumnInfo(name = "fav_date") val date: Date // 创建日期

) {
    @PrimaryKey(autoGenerate = true)
    @ColumnInfo(name = "id")
    var id: Long = 0
}
  • 鞋表:
/**
 * 鞋表
 */
@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
}
  • 注解解释: @Entity 声明这是一个表(实体),主要参数:tableName-表名、foreignKeys-外键、indices-索引。 @ColumnInfo 主要用来修改在数据库中的字段名。 @PrimaryKey 声明该字段主键并可以声明是否自动创建。 @Ignore 声明某个字段只是临时用,不存储在数据库中。 @Embedded 用于嵌套,里面的字段同样会存储在数据库中。

3. 创建 Dao 数据的增删查改。如果想声明一个 Dao,只要在抽象类或者接口加一个 @Dao 注解就行。

  • @Insert 注解声明当前的方法为新增的方法,并且可以设置当新增冲突的时候处理的方法。
@Dao
interface ShoeDao {
    // 省略...
    // 增加一双鞋子
    @Insert(onConflict = OnConflictStrategy.REPLACE)
    fun insertShoe(shoe: Shoe)

    // 增加多双鞋子
    // 除了List之外,也可以使用数组
    @Insert(onConflict = OnConflictStrategy.REPLACE)
    fun insertShoes(shoes: List)
}
  • @Delete注解声明当前的方法是一个删除方法。
@Dao
interface ShoeDao {
    // 省略...
    // 删除一双鞋子
    @Delete
    fun deleteShoe(shoe: Shoe)

    // 删除多个鞋子
    // 参数也可以使用数组
    @Delete
    fun deleteShoes(shoes:List)
}
  • @Update 注解声明当前方法是一个更新方法
@Dao
interface ShoeDao {
    // 省略...
    // 更新一双鞋
    @Update
    fun updateShoe(shoe:Shoe)

    // 更新多双鞋
    // 参数也可以是集合
    @Update
    fun updateShoes(shoes:Array)
}
  • Room 的查很接近原生的SQL语句。@Query注解不仅可以声明这是一个查询语句,也可以用来删除和修改,不可以用来新增。
@Dao
interface 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

    // 模糊查询 排序 同名鞋名查询鞋
    @Query("SELECT * FROM shoe WHERE shoe_name LIKE :name ORDER BY shoe_brand ASC")
    fun findShoesByName(name:String):List

    // 配合LiveData 返回所有的鞋子
    @Query("SELECT * FROM shoe")
    fun getAllShoesLD(): LiveData

    // 配合LiveData 通过Id查询单款鞋子
    @Query("SELECT * FROM shoe WHERE id=:id")
    fun findShoeByIdLD(id: Long): LiveData

    // 配合RxJava 通过Id查询单款鞋子
    @Query("SELECT * FROM shoe WHERE id=:id")
    fun findShoeByIdRx(id: Long): Flowable

    // 省略...
}
  • 复合查询 以 LiveData 为例
@Dao
interface 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
}

4. 创建数据库 创建一个数据库对象是一件非常消耗资源,使用单例可以避免过多的资源消耗。

/**
 * 数据库文件
 */
@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().build()
                        WorkManager.getInstance(context).enqueue(request)
                    }
                })
                .build()
        }
    }
}

@Database注解声明当前是一个数据库文件,注解中entities变量声明数据库中的表(实体),以及其他的例如版本等变量。同时,获取的Dao也必须在数据库类中。完成之后,点击build目录下的make project,系统就会自动帮我创建AppDataBasexxxDao的实现类。

5. 简要封装 这里有必要提醒一下,在不使用 LiveData 和 RxJava 的前提下,Room 的操作是不可以放在主线程中的。

/**
 * 用户处理仓库
 */
class UserRepository private constructor(private val userDao: UserDao) {
    //...

    /**
     * 登录用户 本地数据库的查询
     */
    fun login(account: String, pwd: String):LiveData
            = 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 =
            // ...
    }
}

现在,你就可以愉快的操作本地数据库了。

参考文章: 《Android Room 框架学习》

关注我的视频号,每天都有优质视频教学推送。

微信扫一扫下方二维码即可关注: 在这里插入图片描述

关注
打赏
1658837700
查看更多评论
立即登录/注册

微信扫码登录

0.0420s