Android数据库框架Room: 一对多关系实例(详细,包含增

2022-02-18  本文已影响0人  头发还没秃

Room中,你可以通过两种方式定义和查询实体之间的关系:1、使用具有嵌入式对象的中间数据类;2、具有多重映射返回值类型的关系型查询。

中间数据类

@Dao
interface ContactDao {
    @Query(
        "SELECT * FROM users, phones WHERE users.id = phones.parent_id"
    )
    fun getAll(): LiveData<List<ContactEntity>?>
}

多重映射返回值类型

@Dao
interface UserDao {
    @Query(
        "SELECT * FROM users JOIN phones ON users.id = phones.parent_id"
    )
    fun get(): Map<UserEntity, List<PhoneEntity>?>
}

中间数据类方法可让你避免编写复杂的 SQL 查询,但由于需要额外的数据类,它还可能会导致代码复杂性增加。简而言之,多重映射返回值类型方法需要 SQL 查询完成更多工作;而中间数据类方法需要代码完成更多工作。如果没有使用中间数据类的特定理由,Google建议使用多重映射返回值类型方法。

这里我们仅选择中间数据类方法进行介绍,因为中间数据类有些细节方面需要注意,另外也不需要写太多SQL语句:

@Entity(tableName = "users")
data class UserEntity(
    @PrimaryKey(autoGenerate = true)
    var id: Long = 0,
    @ColumnInfo(name = "name_pinyin")
    var namePinyin: String? = null,//姓名的拼音,根据姓名自动生成
    @ColumnInfo(name = "nick_name")
    var nickName: String? = null,//昵称,选填
) {
    var name: String? = null//姓名,必填
        set(value) {
            val name = value?.trim()
            field = name
            namePinyin = Pinyin.toPinyin(field, "")
        }

    fun getFirstLetter(): String {
        val firstStr = namePinyin?.first()
        return if (ValidatorUtil.isLetter(firstStr.toString())) {//英文
            firstStr.toString().uppercase()
        } else {
            "#"
        }
    }
}

@Entity(tableName = "phones")
data class PhoneEntity(
    @PrimaryKey(autoGenerate = true)
    var id: Long,
    @ColumnInfo(name = "parent_id")//父实体的id
    var parentId: Long,
    var phone: String,//电话号码
)

我们知道一个人可以对应多个电话,可能是公司、住宅、私人、工作等等,为了查询用户和对应的电话号码列表,你必须先在两个实体之间建立一对多关系。为此,创建一个新的数据类,其中每个实例都包含父实体的一个实例和与之对应的所有子实体实例的列表。将 @Relation 注释添加到子实体的实例,同时将 parentColumn 设置为父实体主键列的名称,并将 entityColumn 设置为引用父实体主键的子实体列的名称。

data class ContactEntity (
    @Embedded
    val user: UserEntity,
    @Relation(parentColumn = "id", entityColumn = "parent_id")
    var phones: List<PhoneEntity>? = null,//电话号码
)

然后,新建一个ContactDao,向 DAO 类添加方法,用于返回将父实体与子实体配对的数据类的所有实例。该方法需要 Room 运行两次查询,因此应向该方法添加 @Transaction 注释,以确保整个操作以原子方式执行。

@Dao
interface ContactDao {
    @Transaction
    @Query("SELECT * FROM users")
    fun getAll(): LiveData<List<ContactEntity>?>

    //模糊查询
    @Transaction
    @Query("SELECT * FROM users WHERE name OR name_pinyin LIKE '%' || :searchStr || '%'")
    fun getAllBySearch(searchStr: String): LiveData<List<ContactEntity>?>

    fun insert(db: AppDatabase, contact: ContactEntity) {
        val id = db.contactDataDao().insert(contact.user)
        contact.phones?.forEach {
            it.parentId = id
            db.phoneDao().insert(it)
        }
    }

    fun delete(db: AppDatabase, contact: ContactEntity) {
        db.contactDataDao().delete(contact.user)
        contact.phones?.forEach {
            db.phoneDao().delete(it)
        }
    }
}

最后,我们在 AppDatabase 里面注册它

@Database(
    entities = [
        //这里,这里,这里千万不要把 ContactEntity 加上
        UserEntity::class,
        PhoneEntity::class,
    ],
    version = 1, exportSchema = false
)
abstract class AppDatabase: RoomDatabase() {

    abstract fun contactDao(): ContactDao
    abstract fun userDao(): UserDao
    abstract fun phoneDao(): PhoneDao

    companion object {
        private const val DATABASE_NAME = "Contacts.db"
        @Volatile
        private var INSTANCE: AppDatabase? = null

        fun getInstance(context: Context): AppDatabase = INSTANCE ?: synchronized(this) {
            INSTANCE ?: buildDatabase(context).also {
                INSTANCE = it
            }
        }

        private fun buildDatabase(context: Context) =
            Room.databaseBuilder(context.applicationContext, AppDatabase::class.java, DATABASE_NAME).build()
    }
}

这里需要注意的是 ContactEntity 不需要在 AppDatabase 上注册

以上,我们就把Room的一对多关系建好了。

上一篇下一篇

猜你喜欢

热点阅读