联合查询是要多创建一个实体类么_[译] 如何用 Room 处理一对一,一对多,多对多关系?...
譯者說:最近在做一款 Rss 閱讀器,使用 Room 存儲訂閱源以及其中的文章,這就是一個典型的 一對多 關系。正好通過此文詳細了解 @Relation 注解的使用。
將數據拆分為相關聯的表,并以有意義的方式將數據組合在一起 是設計關系型數據庫的重要部分。從 Room 2.2 (現已穩定)開始,通過 @Relation 注解,我們支持了表之間所有可能的關系:一對一,一對多,多對多 。
一對一
假如我們生活在一個(悲傷的)世界,每個人只能擁有一條狗,并且每條狗也只能有一個主人。這就是一對一關系。為了在關系型數據庫中 表示這一關系,我們創建了兩張表,Dog 和 Owner 。Dog 表持有 owner id 的引用,Owner 表持有 dog id 的引用。在 Room 中,我們創建這樣兩個實體類:
@Entity data class Dog(@PrimaryKey val dogId: Long,val dogOwnerId: Long,val name: String,val cuteness: Int,val barkVolume: Int,val breed: String ) @Entity data class Owner(@PrimaryKey val ownerId: Long, val name: String)我們要在頁面上顯示所有的狗狗和它們的主人,為此,我們創建了一個數據類 DogAndOwner :
data class DogAndOwner(val owner: Owner,val dog: Dog )要通過 Sqlite 完成此次查詢,我們需要:
通過 Room 來得到 List<DogAndOwner> ,我們不需要自己實現兩次查詢和對象映射,僅僅通過 @Relation 注解即可。
在上面的例子中,由于 Dog 擁有主人的信息,所有在 dog 變量上添加 @Relation 注解:指定 owner 表中的 ownerId 和 dog 表中的 dogOwnerId 是相對應的。
data class DogAndOwner(@Embedded val owner: Owner,@Relation(parentColumn = "ownerId",entityColumn = "dogOwnerId")val dog: Dog )Dao 可以簡化如下:
@Transaction @Query("SELECT * FROM Owner") fun getDogsAndOwners(): List<DogAndOwner>注意:由于 Room 會在后臺自動為我們執行這兩次查詢,所以要添加 @Transaction 注解以保證原子性。
一對多
假設一個主人可以擁有多條狗狗 (Yeah !) ,Owner 和 Dog 之間是一對多的關系。之前定義的數據庫結構不需要發生任何變化,我們仍然使用之前的表,因為相關聯的鍵已經在表中了。
現在,為了展示主人和他的狗狗們,我們需要創建一個新的數據類:
data class OwnerWithDogs(val owner: Owner,val dogs: List<Dog> )為了避免兩次查詢,我們給 List<Dog> 添加 @Relation 注解來定義 Dog 和 Owner 之間的一對多關系。
data class OwnerWithDogs(@Embedded val owner: Owner,@Relation(parentColumn = "ownerId",entityColumn = "dogOwnerId")val dogs: List<Dog> )Dao 是這樣的。
@Transaction @Query("SELECT * FROM Owner") fun getDogsAndOwners(): List<OwnerWithDogs>多對多
現在假設我們生活在一個完美的世界,每個主人可以擁有多條狗,每條狗也可以有多個主人。要對此關系進行建模,僅僅通過 Dog 表和 Owner 表是不夠的。由于一條狗可能有多個主人,所以同一個 dogId 可能需要多條數據,以匹配不同的主人。但是在 Dog 表中,dogId 是主鍵,我們不能插入多個 id 相同,主人不同的狗狗。為了解決這一問題,我們需要額外創建一個存儲 (dogId,ownerId) 的 關聯表 (也稱為交叉引用表) 。
@Entity(primaryKeys = ["dogId", "ownerId"]) data class DogOwnerCrossRef(val dogId: Long,val ownerId: Long )假設現在僅僅只通過 Sqlite 來所有的主人和他們的狗:List<OwnerWithDogs> ,我們需要兩次查詢:獲取所有的主人,聯表查詢 Dog 表和 DogOwnerCrossRef 表。
SELECT * FROM Owner SELECTDog.dogId AS dogId,Dog.dogOwnerId AS dogOwnerId,Dog.name AS name,_junction.ownerId FROMDogOwnerCrossRef AS _junction INNER JOIN Dog ON (_junction.dogId = Dog.dogId)用 Room 實現的話,我們需要更新 OwnerWithDogs 實例類,告訴 Room 要為了獲取對應的所有狗狗,要關聯表 DogOwnerCrossRef 。通過 Junction 來引用表。
在 Dao 中,通過查詢 Owner 來返回正確的數據類。
@Transactionhttps://youtu.be/_aJsh6P00c0 @Query("SELECT * FROM Owner") fun getOwnersWithDogs(): List<OwnerWithDogs>高級用法示例
當使用 @Relation 注解時,Room 根據被注解的屬性類型來推斷使用哪個實體類。例如,到目前為止,我們給 Dog 或 List<Dog> 添加了注解,這就告訴了 Room 要使用哪個類,要查詢哪些字段。
如果我們想返回一個其他對象,例如 Pup,它不是一個實體但是包含了一些字段。我們可以通過 @Relation 注解指定要使用的實體。
data class Pup(val name: String,val cuteness: Int = 11 ) data class OwnerWithPups(@Embedded val owner: Owner,@Relation(parentColumn = "ownerId",entity = Dog::class,entityColumn = "dogOwnerId")val dogs: List<Pup> )如果我們指向返回實體類的指定字段,就需要通過 @Relation 注解的 projection 屬性來指定。例如,我們指向獲取 OwnerWithDogs中所有狗狗的名字,因此我們需要返回的是 List<String> 。而 Room 無法推斷這些字符串代表的是名字還是品種,所有需要我們通過 projection 指定。
data class OwnerWithDogs(@Embedded val owner: Owner,@Relation(parentColumn = "ownerId",entity = Dog::class,entityColumn = "dogOwnerId",projection = ["name"])val dogNames: List<String> )如果你想在 dogOwnerId 和 ownerId 之間定義更加嚴格的關系,獨立于你所創建的任何關系,可以在這些字段之間添加 ForeignKey 約束。請記住,SQLite 外鍵定義索引,并且可以具有級聯觸發器來更新或刪除表中的條目。因此,請根據是否希望在數據庫中使用這種功能來決定是否要使用外鍵。
無論你需要一對一,一對多,還是多對多的支持,Room 都可以通過 @Relation 注釋滿足你。
總結
以上是生活随笔為你收集整理的联合查询是要多创建一个实体类么_[译] 如何用 Room 处理一对一,一对多,多对多关系?...的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: webbrowser控件 有数据 但页面
- 下一篇: vf6.0 如何把命令窗口字体变大些_终