假设我们生活在一个每个人只能拥有一只狗,且每只狗只能有一个主人的 “悲惨世界” 中,这就是一对一关系。如果要以关系型数据库的方式来反应它的话,我们可以创建两张表: Dog 表和 Owner 表,其中 Dog 表通过 owner id 来引用 Owner 表中的数据,或者 Owner 表通过 dog id 来引用 Dog 表中的数据。
1 2 3 4 5 6 7 8 9 10 11 12
@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 类:
1 2 3 4
data class DogAndOwner( val owner: Owner, val dog: Dog )
为了在 SQLite 中进行查询,我们需要 1) 运行两个查询: 一个获取所有的主人数据,一个获取所有的狗狗数据,2) 根据 owner id 来进行数据的关系映射。
1 2 3
SELECT * FROM Owner
SELECT * FROM Dog WHERE dogOwnerId IN (ownerId1, ownerId2, …)
要在 Room 中获取一个 List ,我们不需要自己去实现上面说的查询和映射,只需要使用 @Relation 注解。
在我们的示例中,由于 Dog 有了 owner 的信息,我们给 dog 变量增加 @Relation 注解,指定父级 (这里对应 Owner) 上的 ownerId 列对应 dogOwnerId:
1 2 3 4 5 6 7 8
data class DogAndOwner( @Embedded val owner: Owner, @Relation( parentColumn = "ownerId", entityColumn = "dogOwnerId" ) val dog: Dog )
Dao https://developer.android.google.cn/reference/androidx/room/Dao @Transaction https://developer.android.google.cn/reference/androidx/room/Transaction.html
data class OwnerWithDogs( val owner: Owner, val dogs: List<Dog> )
为了避免运行两个独立的查询,我们可以在 Dog 和 Owner 中定义一对多的关系,同样,还是在 List 前增加 @Relation 注解。
1 2 3 4 5 6 7 8
data class OwnerWithDogs( @Embedded val owner: Owner, @Relation( parentColumn = "ownerId", entityColumn = "dogOwnerId" ) val dogs: List<Dog> )
现在,Dao 类又变成了这样:
1 2 3 4
@Transaction @Query("SELECT * FROM Owner")
fun getDogsAndOwners(): List<OwnerWithDogs>
多对多关系
现在,继续假设我们生活在一个完美的世界中,一个人可以拥有多只狗,每只狗可以拥有多个主人。要对这个关系进行映射,之前的 Dog 和 Owner 表是不够的。由于一只狗狗可以有多个主人,我们需要在同一个 dog id 上能够匹配多个不同的 owner id。由于 dogId 是 Dog 表的主键,我们不能直接在 Dog 表中添加同样 id 的多条数据。为了解决这个问题,我们需要创建一个 associative 表 (也被称为连接表),这个表来存储 (dogId, ownerId) 的数据对。
1 2 3 4 5
@Entity(primaryKeys = ["dogId", "ownerId"]) data class DogOwnerCrossRef( val dogId: Long, val ownerId: Long )
associative
1
https://en.wikipedia.org/wiki/Associative_entity
如果现在我们想要获取到所有的狗狗和主人的数据,也就是 List,仅需要编写两个 SQLite 查询,一个获取到所有的主人数据,另一个获取 Dog 和 DogOwnerCrossRef 表的连接数据。
1 2 3 4 5 6 7 8 9 10 11
SELECT * FROM Owner SELECT Dog.dogId AS dogId, Dog.dogOwnerId AS dogOwnerId, Dog.name AS name, _junction.ownerId FROM DogOwnerCrossRef AS _junction INNER JOIN Dog ON (_junction.dogId = Dog.dogId)
WHERE _junction.ownerId IN (ownerId1, ownerId2, …)
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> )