原文作者:Florina Muntenescu 原文地址: medium.com/androiddeve… 译者:秉心说
译者说:最近在做一款 Rss 阅读器,使用 Room 存储订阅源以及其中的文章,这就是一个典型的 一对多 关系。正好通过此文详细了解 @Relation 注解的使用。

将数据拆分为相关联的表,并以有意义的方式将数据组合在一起 是设计关系型数据库的重要部分。从 Room 2.2 (现已稳定)开始,通过 @Relation 注解,我们支持了表之间所有可能的关系:一对一一对多多对多

一对一

假如我们生活在一个(悲伤的)世界,每个人只能拥有一条狗,并且每条狗也只能有一个主人。这就是一对一关系。为了在关系型数据库中 表示这一关系,我们创建了两张表,DogOwnerDog 表持有 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 完成此次查询,我们需要:

  1. 进行两次查询,先查询出所有的主人,然后在根据主人的 owner id 查询出所有的狗
  2. 处理对象映射
SELECT * FROM Owner
SELECT * FROM DogWHERE dogOwnerId IN (ownerId1, ownerId2, …)

通过 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 !) ,OwnerDog 之间是一对多的关系。之前定义的数据库结构不需要发生任何变化,我们仍然使用之前的表,因为相关联的键已经在表中了。

现在,为了展示主人和他的狗狗们,我们需要创建一个新的数据类:

data class OwnerWithDogs(val owner: Owner,val dogs: List<Dog>
)

为了避免两次查询,我们给 List<Dog> 添加 @Relation 注解来定义 DogOwner 之间的一对多关系。

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 根据被注解的属性类型来推断使用哪个实体类。例如,到目前为止,我们给 DogList<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>
)

如果你想在 dogOwnerIdownerId 之间定义更加严格的关系,独立于你所创建的任何关系,可以在这些字段之间添加 ForeignKey 约束。请记住,SQLite 外键定义索引,并且可以具有级联触发器来更新或删除表中的条目。因此,请根据是否希望在数据库中使用这种功能来决定是否要使用外键。

无论你需要一对一,一对多,还是多对多的支持,Room 都可以通过 @Relation 注释满足你。

联合查询是要多创建一个实体类么_[译] 如何用 Room 处理一对一,一对多,多对多关系?...相关推荐

  1. java 怎么快速创建实体类_java编程使用eclipse如何快速创建一个实体类

    一. 创建一个实体类如下package pojo; public class GoodsModel { private String goodsname; private double goodspr ...

  2. Java练习题 类 先创建一个Point类,然后定义Trianglele类。在Trianglele类中定义三个Point的实体来表示一个三角形的三个点,然后定义两个方法求三角形的周长、面积。

    先创建一个Point类,然后定义Trianglele类. 在Trianglele类中定义三个Point的实体来表示一个三角形的三个点,再定义构造方法对这三个点进行初始化,然后定义两个方法求三角形的周长 ...

  3. java定义三角类_Java先创建一个point类然后定义trianglete类,在trianglete类中定义三个point的实体表示三角形的三个点。...

    Java先创建一个point类然后定义trianglete类,在trianglete类中定义三个point的实体表示三角形的三个点. 关注:197  答案:1  mip版 解决时间 2021-01-3 ...

  4. python计算长方体体积编程_计算体积 编程创建一个Box类(长方体) 联合开发网 - pudn.com...

    计算体积 所属分类:Windows编程 开发工具:Java 文件大小:4KB 下载次数:0 上传日期:2019-12-08 16:53:01 上 传 者:洛水天依- 说明:  编程创建一个Box类(长 ...

  5. Entity Framework 的小实例:在项目中添加一个实体类,并做插入操作

    Entity Framework 的小实例:在项目中添加一个实体类,并做插入操作 Entity Framework 的小实例:在项目中添加一个实体类,并做插入操作 1>. 创建一个控制台程序 2 ...

  6. Android自定义控件学习(四)------创建一个视图类

    创建一个视图类 精心设计的自定义视图与其他精心设计的类非常相似.它使用易于使用的界面封装了一组特定的功能,它可以高效地使用CPU和内存,等等.不过,作为一个设计良好的设计,自定义视图应该: 符合And ...

  7. android自定义View学习(一)----创建一个视图类

    创建一个视图类 精心设计的自定义视图与其他精心设计的类非常相似.它使用易于使用的界面封装了一组特定的功能,它可以高效地使用CPU和内存,等等.不过,作为一个设计良好的班级,自定义视图应该: 符合And ...

  8. 《JAVA练习题目9》 创建一个Person类和一个Family类。(这次的OJ实在写的没有状态,被样例搞崩了,这次代码还有好多代码复制等一系列不良现象,就是为了过OJ写的。。大家凑活看一下吧)

    题目内容: 创建一个Person类和一个Family类.其中, Person类包含编号id(整型).名字name(字符串).性别gender(字符串)三个属性,和一个初始化对象所有属性的有参构造方法, ...

  9. 发布一个实体类属性生成小工具,给开发加点料

    做了很久的代码生成工具,基本上都是基于表生成实体类属性的,把数据库表的信息拿出来,然后之乎者也后生成一个标准的实体类,包含字段.属性.描述等东西. 是基于整个数据库生成整个框架代码的工具,如我的代码生 ...

最新文章

  1. andriod之对话框--标准对话框、列表对话框、自定义对话框
  2. 计算机网络第七章:网络安全
  3. 闲鱼研发框架应用和探索
  4. Linux内核 获取本机mac,Linux获取本机MAC地址
  5. java实现并发连接池_java并发实战:连接池实现
  6. Java虚拟机(六)——虚拟机栈
  7. Oracle10g BIGFILE表空间带来的好处
  8. 纽约客封面故事:欢迎来到「黑暗工厂」,这里是由机器统治的世界
  9. 网站安全检测:8款非常有用的免费 Web 安全测试工具
  10. 简单的走迷宫小游戏(C语言编写,无指针,无链表)
  11. 机器学习--归纳总结
  12. win7计算机属性里面的图标变白,Win7系统桌面图标白边如何解决
  13. 计算机锁屏域策略,域组策略锁屏界面设置不生效
  14. android获取整体存储空间大小,Android 获取剩余存储空间
  15. 360搜索结果页html代码,360搜索抓取完全封闭网页揭秘
  16. 优秀网页翻译:高精度 10MHz GPS 驯服钟 (GPSDO) - Part 1
  17. PHP检测及判断手机登录用户是安卓或爱疯(iPhone)客户端
  18. Tomcat服务器修改网站显示图标
  19. 敬业签安卓手机便签如何使用本机号码一键登录?
  20. Thinkphp 源码分析1

热门文章

  1. python解压文件到指定路径
  2. windows下安装服务注册Consul
  3. reactjs组件的生命周期:创建时和更新时
  4. Linux split命令:拆分大的tar压缩包然后解压
  5. redis-cli报错:(error) MOVED 12706 (未以集群模式连接)
  6. Android Studio3.5.2离线安装gradle
  7. openresty获取nginx请求头信息
  8. 简单讲解一下负载均衡、反向代理模式的优点、缺点
  9. spring事务--使用aop事务代理对象调用方法示例
  10. Spring注解开发-Bean注册