在 Room中,你可以通過(guò)兩種方式定義和查詢實(shí)體之間的關(guān)系:1障斋、使用具有嵌入式對(duì)象的中間數(shù)據(jù)類贞瞒;2、具有多重映射返回值類型的關(guān)系型查詢摆寄。
中間數(shù)據(jù)類
@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>?>
}
中間數(shù)據(jù)類方法可讓你避免編寫(xiě)復(fù)雜的 SQL 查詢失暴,但由于需要額外的數(shù)據(jù)類,它還可能會(huì)導(dǎo)致代碼復(fù)雜性增加微饥。簡(jiǎn)而言之逗扒,多重映射返回值類型方法需要 SQL 查詢完成更多工作;而中間數(shù)據(jù)類方法需要代碼完成更多工作欠橘。如果沒(méi)有使用中間數(shù)據(jù)類的特定理由矩肩,Google建議使用多重映射返回值類型方法。
這里我們僅選擇中間數(shù)據(jù)類方法進(jìn)行介紹肃续,因?yàn)橹虚g數(shù)據(jù)類有些細(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,//昵稱,選填
) {
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,//電話號(hào)碼
)
我們知道一個(gè)人可以對(duì)應(yīng)多個(gè)電話畦韭,可能是公司、住宅肛跌、私人艺配、工作等等,為了查詢用戶和對(duì)應(yīng)的電話號(hào)碼列表衍慎,你必須先在兩個(gè)實(shí)體之間建立一對(duì)多關(guān)系转唉。為此,創(chuàng)建一個(gè)新的數(shù)據(jù)類稳捆,其中每個(gè)實(shí)例都包含父實(shí)體的一個(gè)實(shí)例和與之對(duì)應(yīng)的所有子實(shí)體實(shí)例的列表赠法。將 @Relation
注釋添加到子實(shí)體的實(shí)例,同時(shí)將 parentColumn
設(shè)置為父實(shí)體主鍵列的名稱乔夯,并將 entityColumn
設(shè)置為引用父實(shí)體主鍵的子實(shí)體列的名稱砖织。
data class ContactEntity (
@Embedded
val user: UserEntity,
@Relation(parentColumn = "id", entityColumn = "parent_id")
var phones: List<PhoneEntity>? = null,//電話號(hào)碼
)
然后,新建一個(gè)ContactDao末荐,向 DAO 類添加方法侧纯,用于返回將父實(shí)體與子實(shí)體配對(duì)的數(shù)據(jù)類的所有實(shí)例。該方法需要 Room 運(yùn)行兩次查詢甲脏,因此應(yīng)向該方法添加 @Transaction
注釋眶熬,以確保整個(gè)操作以原子方式執(zhí)行。
@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)
}
}
}
最后块请,我們?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)系建好了牍白。