sqlite 表与表之间的关系_Room 中的数据库关系

本文介绍了Android Room库中如何处理表与表之间的关系,包括一对一、一对多和多对多的关系实现,引用了@Relation注解和Junction类。同时提到了SQLite的外键概念,并链接到Room 2.2的更新内容。读者可以通过Dao和@Transaction来理解如何在数据库操作中处理复杂关系。
摘要由CSDN通过智能技术生成
设计一个关系型数据库很重要的一部分是将数据拆分成具有相关关系的数据表,然后将数据以符合这种关系的逻辑方式整合到一起。从 Room 2.2 的稳定版开始,我们可利用一个 @Relation 注解来支持表之间所有可能出现的关系: 一对一、一对多和多对多。
  • Room 2.2

    https://developer.android.google.cn/jetpack/androidx/releases/room#version_220_3

  • @Relation

    https://developer.android.google.cn/reference/android/arch/persistence/room/Relation

一对一关系

51a9b244e6da753259256592cbf73362.png

一对一关系

假设我们生活在一个每个人只能拥有一只狗,且每只狗只能有一个主人的 “悲惨世界” 中,这就是一对一关系。如果要以关系型数据库的方式来反应它的话,我们可以创建两张表: Dog 表和 Owner 表,其中 Dog 表通过 owner id 来引用 Owner 表中的数据,或者 Owner 表通过 dog id 来引用 Dog 表中的数据。
@Entitydata class Dog(    @PrimaryKey val dogId: Long,    val dogOwnerId: Long,    val name: String,    val cuteness: Int,    val barkVolume: Int,    val breed: String)@Entitydata class Owner(@PrimaryKey val ownerId: Long, val name: String)
假设我们想在一个列表中展示所有的狗和它们的主人,我们需要创建一个 DogAndOwner 类:
data class DogAndOwner(    val owner: Owner,    val dog: Dog)
为了在 SQLite 中进行查询,我们需要 1) 运行两个查询: 一个获取所有的主人数据,一个获取所有的狗狗数据,2) 根据 owner id 来进行数据的关系映射。
SELECT * FROM OwnerSELECT * FROM Dog WHERE dogOwnerId IN (ownerId1, ownerId2, …)
要在 Room 中获取一个 List ,我们不需要自己去实现上面说的查询和映射,只需要使用 @Relation 注解。 在我们的示例中,由于 Dog 有了 owner 的信息,我们给 dog 变量增加 @Relation 注解,指定父级 (这里对应 Owner ) 上的 ownerId 列对应 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
注意: 由于 Room 会默默的帮我们运行两个查询请求,因此需要增加 @Transaction 注解来确保这个行为是原子性的。
  • Daohttps://developer.android.google.cn/reference/androidx/room/Dao
  • @Transactionhttps://developer.android.google.cn/reference/androidx/room/Transaction.html

一对多关系

320c10f0a838c90dfa01aacbb922b9d4.png

一对多关系 再假设,一个主人可以养多只狗狗,现在上面的关系就变成了一对多关系。我们之前定义的数据库 schema 并不需要改变,仍然使用同样的表结构,因为在 “多” 这一方的表中已经有了关联键。 现在,要展示狗和主人的列表,我们需要创建一个新的类来进行建模:
data class OwnerWithDogs(    val owner: Owner,    val dogs: List)
为了避免运行两个独立的查询,我们可以在 Dog 和 Owner 中定义一对多的关系,同样,还是在 List 前增加 @Relation 注解。
data class OwnerWithDogs(     @Embedded val owner: Owner,     @Relation(          parentColumn = "ownerId",          entityColumn = "dogOwnerId"     )     val dogs: List)
现在, Dao 类又变成了这样:
@Transaction@Query("SELECT * FROM Owner")fun getDogsAndOwners(): List

多对多关系

eff9c59a338532f7ecd61b6f37995ed5.png

多对多关系

现在,继续假设我们生活在一个完美的世界中,一个人可以拥有多只狗,每只狗可以拥有多个主人。要对这个关系进行映射,之前的 Dog 和 Owner 表是不够的。由于一只狗狗可以有多个主人,我们需要在同一个 dog id 上能够匹配多个不同的 owner id。由于 dogId 是 Dog 表的主键,我们不能直接在 Dog 表中添加同样 id 的多条数据。为了解决这个问题,我们需要创建一个 associative 表 (也被称为连接表),这个表来存储 ( dogId , ownerId ) 的数据对。
@Entity(primaryKeys = ["dogId", "ownerId"])data class DogOwnerCrossRef(    val dogId: Long,    val ownerId: Long)
  • associative

    https://en.wikipedia.org/wiki/Associative_entity

如果现在我们想要获取到所有的狗狗和主人的数据,也就是 List ,仅需要编写两个 SQLite 查询,一个获取到所有的主人数据,另一个获取 Dog 和 DogOwnerCrossRef 表的连接数据。
SELECT * FROM OwnerSELECT     Dog.dogId AS dogId,     Dog.dogOwnerId AS dogOwnerId,     Dog.name AS name,     _junction.ownerIdFROM     DogOwnerCrossRef AS _junctionINNER JOIN Dog ON (_junction.dogId = Dog.dogId)WHERE _junction.ownerId IN (ownerId1, ownerId2, …)
要通过 Room 来实现这个功能,我们需要更新 OwnerWithDogs 数据类,并告诉 Room 要使用 DogOwnerCrossRef 这个连接表来获取 Dogs 数据。我们通过使用 Junction 引用这张表。
data class OwnerWithDogs(    @Embedded val owner: Owner,    @Relation(         parentColumn = "ownerId",         entityColumn = "dogId",         associateBy = Junction(DogOwnerCrossRef::class)    )    val dogs: List)
  • Junction

    https://developer.android.google.cn/reference/androidx/room/Junction

在我们的 Dao 中,我们需要从 Owners 中选择并返回正确的数据类:

@Transaction@Query("SELECT * FROM Owner")fun getOwnersWithDogs(): List

更高阶的数据库关系用例

当使用 @Relation 注解时,Room 会默认从所修饰的属性类型推断出要使用的数据库实体。例如,到目前为止我们用 @Relation 修饰了 Dog  (或者是 List ),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)
如果我们只想从数据库实体中返回特定的列,您需要通过在 @Relation 中的 projection 属性中定义要返回哪些列。例如,假如我们只想获取 OwnerWithDogs 数据类中所有狗的名字,由于我们需要用到 List ,Room 不能推断出这些字符串是对应于狗的品种呢还是狗的名字,因此我们需要在 projection 属性中指名。
data class OwnerWithDogs(     @Embedded val owner: Owner,     @Relation(           parentColumn = "ownerId",           entity = Dog::class,           entityColumn = "dogOwnerId",           projection = ["name"]     )     val dogNames: List)
如果您想在 dogOwnerId 和 ownerId 中定义更严格的关系,而不管您所创建的是什么,您可以通过在字段中使用 ForeignKey 来做到。记住, SQLite 中的外键 会创建索引,并且会在更新或者删除表中数据时做级联操作。因此您要根据实际情况来判断是否使用外键功能。
  • ForeignKeyhttps://developer.android.google.cn/reference/androidx/room/ForeignKey.html
  • SQLite 中的外键https://sqlite.org/foreignkeys.html
不管您是要使用一对一,一对多还是多对多关系,Room 都会为您提供 @Relation 注解来解决问题。您可以在我们的 Android Dev Summit ’19 的一个演讲中了解有关 Room 2.2 的更多新功能:
  • @Relationhttps://developer.android.google.cn/reference/androidx/room/Relation.html
  • Room 2.2 的更多新功能https://developer.android.google.cn/jetpack/androidx/releases/room#version_220_3

7558594d08484566362ad976feb4720f.gif 点击屏末  | 进一步了解 Room

a72b86e1d1df00ebea6f10647331f12d.png

88993c47a1cf548a079e1d78c2d0e0f3.gif

 想了解更多 Android 内容?

  • 在公众号首页发送关键词 "Android",获取相关历史技术文章;

  • 在公众号首页发送关键词 "ADS",获取开发者峰会演讲中文字幕视频;

  • 还有更多疑惑?欢迎点击菜单 "联系我们" 反馈您在开发过程中遇到的问题。

推荐阅读

3e89a6dfb462477d14ca82979d82be79.png

fc4ab3b63a3cfa8d4326cc950c3be345.png

fff01fc7ea76ef5311f5d39f91a91d52.png


380ea15a62cd1a090310f42440c17e5a.gif

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值