Android數(shù)據(jù)庫(kù)框架Room: 一對(duì)多關(guān)系實(shí)例(詳細(xì),包含增刪改查)

Room中,你可以通過(guò)兩種方式定義和查詢(xún)實(shí)體之間的關(guān)系:1、使用具有嵌入式對(duì)象的中間數(shù)據(jù)類(lèi);2、具有多重映射返回值類(lèi)型的關(guān)系型查詢(xún)。

中間數(shù)據(jù)類(lèi)

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

多重映射返回值類(lèi)型

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

中間數(shù)據(jù)類(lèi)方法可讓你避免編寫(xiě)復(fù)雜的 SQL 查詢(xún),但由于需要額外的數(shù)據(jù)類(lèi),它還可能會(huì)導(dǎo)致代碼復(fù)雜性增加。簡(jiǎn)而言之,多重映射返回值類(lèi)型方法需要 SQL 查詢(xún)完成更多工作;而中間數(shù)據(jù)類(lèi)方法需要代碼完成更多工作。如果沒(méi)有使用中間數(shù)據(jù)類(lèi)的特定理由,Google建議使用多重映射返回值類(lèi)型方法。

這里我們僅選擇中間數(shù)據(jù)類(lèi)方法進(jìn)行介紹,因?yàn)橹虚g數(shù)據(jù)類(lèi)有些細(xì)節(jié)方面需要注意,另外也不需要寫(xiě)太多SQL語(yǔ)句:

@Entity(tableName = "users")
data class UserEntity(
    @PrimaryKey(autoGenerate = true)
    var id: Long = 0,
    @ColumnInfo(name = "name_pinyin")
    var namePinyin: String? = null,//姓名的拼音,根據(jù)姓名自動(dòng)生成
    @ColumnInfo(name = "nick_name")
    var nickName: String? = null,//昵稱(chēng),選填
) {
    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")//父實(shí)體的id
    var parentId: Long,
    var phone: String,//電話(huà)號(hào)碼
)

我們知道一個(gè)人可以對(duì)應(yīng)多個(gè)電話(huà),可能是公司、住宅、私人、工作等等,為了查詢(xún)用戶(hù)和對(duì)應(yīng)的電話(huà)號(hào)碼列表,你必須先在兩個(gè)實(shí)體之間建立一對(duì)多關(guān)系。為此,創(chuàng)建一個(gè)新的數(shù)據(jù)類(lèi),其中每個(gè)實(shí)例都包含父實(shí)體的一個(gè)實(shí)例和與之對(duì)應(yīng)的所有子實(shí)體實(shí)例的列表。將 @Relation 注釋添加到子實(shí)體的實(shí)例,同時(shí)將 parentColumn 設(shè)置為父實(shí)體主鍵列的名稱(chēng),并將 entityColumn 設(shè)置為引用父實(shí)體主鍵的子實(shí)體列的名稱(chēng)。

data class ContactEntity (
    @Embedded
    val user: UserEntity,
    @Relation(parentColumn = "id", entityColumn = "parent_id")
    var phones: List<PhoneEntity>? = null,//電話(huà)號(hào)碼
)

然后,新建一個(gè)ContactDao,向 DAO 類(lèi)添加方法,用于返回將父實(shí)體與子實(shí)體配對(duì)的數(shù)據(jù)類(lèi)的所有實(shí)例。該方法需要 Room 運(yùn)行兩次查詢(xún),因此應(yīng)向該方法添加 @Transaction 注釋?zhuān)源_保整個(gè)操作以原子方式執(zhí)行。

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

    //模糊查詢(xún)
    @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)
        }
    }
}

最后,我們?cè)?AppDatabase 里面注冊(cè)它

@Database(
    entities = [
        //這里,這里,這里千萬(wàn)不要把 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 上注冊(cè)

以上,我們就把Room的一對(duì)多關(guān)系建好了。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容