@


# 前言

官方简介:

Room 持久性库在 SQLite 的基础上提供了一个抽象层,让用户能够在充分利用 SQLite 的强大功能的同时,获享更强健的数据库访问机制。

引入库:

implementation "androidx.room:room-runtime:2.3.0"
kapt "androidx.room:room-compiler:2.3.0"
//kotlin 扩展库
implementation "androidx.room:room-ktx:2.3.0"

配置 build:

android {
    ...
    defaultConfig {
        ...
        javaCompileOptions {
            annotationProcessorOptions {
                arguments += mapOf(
                    "room.schemaLocation" to "$projectDir/schemas",
                    "room.incremental" to "true",
                    "room.expandProjection" to "true"
                )
            }
        }
    }
}

提示:以下是本篇文章正文内容,下面案例可供参考

一、简单使用

万事开头难, 好记性不如烂笔头. 看十遍不如敲一遍.
所以 咱们先把代码敲起来.

1.Entity

@Entity
class RoomTwoEntity {
    @PrimaryKey
    var id: String = ""
    @ColumnInfo
    var nickname: String? = null
    @ColumnInfo
    var sex: Int = 0

    @Ignore
    var like = false
}
  • @Entity: 表示数据库中的表
  • @ColumnInfo: 表示table中的字段
  • @Ignore: 表示忽略该字段, (不映射到表中)

细节后面再讲

2.Dao

@Dao
interface RoomTwoDao {
    @Query("select * from RoomTwoEntity")
    fun get(): MutableList<RoomTwoEntity>

    @Insert(onConflict = OnConflictStrategy.REPLACE)    //当冲突时: ABORT,取消;  REPLACE,替换;  IGNORE,忽略;
    fun add(entity: RoomTwoEntity)

    @Insert(onConflict = OnConflictStrategy.REPLACE)    //当冲突时: ABORT,取消;  REPLACE,替换;  IGNORE,忽略;
    fun addList(list: MutableList<RoomTwoEntity>)

    @Delete
    fun delete(entity: RoomTwoEntity)

    @Update
    fun update(entity: RoomTwoEntity)

    @Query("delete  from RoomTwoEntity where id = :id ")    //删除也可以用 query
    fun deleteById(id: String)
}

这代码是不是很好阅读!
普通增删改只需要一个注解. 查询需要写 sql


3.DataBase

@Database(entities = [RoomTwoEntity::class], version = 1)
abstract class RoomTestDatabase : RoomDatabase() {
    abstract fun roomTwoDao(): RoomTwoDao

    companion object {
        private var instance: RoomTestDatabase? = null
        fun getInstance(context: Context): RoomTestDatabase {
            if (instance == null) {
                instance = Room.databaseBuilder(
                    context.applicationContext,
                    RoomTestDatabase::class.java,
                    "Test.db" //数据库名称
                )
//                    .allowMainThreadQueries()   //主线程中执行
                    .fallbackToDestructiveMigration() //数据稳定前, 重建.
//                    .addMigrations(MIGRATION_1_2) //版本升级
                    .build()
            }
            return instance!!
        }
    }
}

4.使用

注意: 代码要在子线程中执行. 或者方便测试时打开 .allowMainThreadQueries()

//创建 10 条数据
private fun createTen(){
    val list = mutableListOf<RoomTwoEntity>()
    repeat(10){
        list.add(RoomTwoEntity().apply {
            id = "room$it"
            nickname = "名字$it"
        })
    }
    RoomTestDatabase.getInstance(mActivity).roomTwoDao().addList(list)
}

//查询数据
private fun query(): MutableList<RoomTwoEntity>{
    return RoomTestDatabase.getInstance(mActivity).roomTwoDao().get()
}

//例如
GlobalScope.launch(Dispatchers.IO) {
    createTen()
}

二、参数解析

1.Entity

1.1 @Entity

1.2 @ColumnInfo
这个注解默认可以省略

1.3 @PrimaryKey

主键必须有, 且必须注解标出
每个实体必须将至少 1 个字段定义为主键。即使只有 1 个字段,您仍然需要为该字段添加 @PrimaryKey 注释。此外,如果您想让 Room 为实体分配自动 ID,则可以设置 @PrimaryKey 的 autoGenerate 属性。如果实体具有复合主键,您可以使用 @Entity 注释的 primaryKeys 属性.

2.Dao

2.1 onConflict

@Insert @Update 时:
新增或修改时, 假设与原有行数据冲突(例如主键冲突). 时的处理方式

2.2 还可以这样用:

@Dao
interface MyDao {
    @Insert(onConflict = OnConflictStrategy.REPLACE)
    fun insertUsers(vararg users: User)	// java: (User... users)

    @Insert
    fun insertBothUsers(user1: User, user2: User) //多实体

    @Insert
    fun insertUsersAndFriends(user: User, friends: List<User>)

    @Update
    //可以让它返回一个 Int, 代表数据库中更新的行数
    fun updateUsers(vararg users: User): Int

    @Delete
    fun deleteUsers(vararg users: User): Int	//返回行数
}

2.3 @Query

以下搬运自官方文章:
@Query 是 DAO 类中使用的主要注释。它允许您对数据库执行读/写操作。每个 @Query 方法都会在编译时进行验证,因此如果查询出现问题,则会发生编译错误,而不是运行时失败。

Room 还会验证查询的返回值,以确保当返回的对象中的字段名称与查询响应中的对应列名称不匹配时,Room 可以通过以下两种方式之一提醒您:

  • 如果只有部分字段名称匹配,则会发出警告。
  • 如果没有任何字段名称匹配,则会发出错误。

3.查询方式

它可以这样查: 冒号后写参数名.

@Query("SELECT * FROM user WHERE age > :minAge")
fun loadAllUsersOlderThan(minAge: Int): Array<User>

也可以这样查: 只查部分字段

@Query("SELECT first_name, last_name FROM user")
fun loadFullName(): List<NameTuple>

还可以这样查: 传入参数集合

@Query("SELECT first_name, last_name FROM user WHERE region IN (:regions)")
fun loadUsersFromRegions(regions: List<String>): List<NameTuple>

更可以这样查: 连表查询,复杂查询等

@Query(
    "SELECT user.name AS userName, pet.name AS petName " +
    "FROM user, pet " +
    "WHERE user.id = pet.user_id"
)
fun loadUserAndPetNames(): LiveData<List<UserPet>>

// You can also define this class in a separate file.
data class UserPet(val userName: String?, val petName: String?)

虽然博主没测, 但这里 UserPet 估计是不需要 @Entity 的;

开启事务
@Transaction

@Dao
abstract class UsersDao {
	@Transaction
	open suspend fun setLoggedInUser(loggedInUser: User) {
	    deleteUser(loggedInUser)
	    insertUser(loggedInUser)
	}

	@Query("DELETE FROM users")
	abstract fun deleteUser(user: User)

	@Insert
	abstract suspend fun insertUser(user: User)
}

因为DB操作必须要在子线程:
可以将 suspend Kotlin 关键字添加到 DAO 方法中,以使用 Kotlin 协程功能使这些方法成为异步方法。这样可确保不会在主线程上执行这些方法。

就写到这把, 篇幅有点长了, 剩下的下一篇再讲.

总结

room的使用可比直接用SQLite简单多了. 而且方便升级. 还能够配合Paging, 配合LiveData, FLow等使用.

上一篇: Paging3-分页数据加载库(结合room)
下一篇: Room-数据持久化存储(进阶)

06-28 18:43