@OneToOne (bidirectional)
最后更新于
最后更新于
假设有学生和学费两张表:
student | tuition |
---|---|
一个学生只能有一个学费要缴纳,一个学费也只要求特定的一名学生缴纳。所以这是一个一对一关系。
概念:
关系的拥有者/关系的维护者/owning side
拥有外键的表即是关系的拥有者,并且还是关系的维护者。
Parent Table/non-owning side
Parent/non-owning side | Child/owning side | OK? |
---|---|---|
有孩子,没爸妈,那当然是不可能的。
对于 student 和 tuition 也是这样。如果有一个 tuition 没有 student,那是不可能的,都没有学生哪来的收学费这一说呢。
在这种情形下 student 表是 Parent Table。
关系的拥有者一般都是 Child Table,而不是 Parent Table。
来看代码:
@OneToOne
mappedBy = "student"
如果不在 Parent Table 或者说 non-owning side 使用 mappedBy
的话,这就是一个单项关系(虽然 @OneToOne
不存在单项关系)。mappedBy
的值应该是 owning side 外键的实例域名(实际来自 getters 或 setters)。
cascade = CascadeType.ALL
cascade
会赋予当前实体操作另一个实体内容的权限。
JPA Cascade Types:
CascadeType.ALL
所有级联操作
拥有全部权限。
CascadeType.REMOVE
级联删除操作
删除当前实体时,与它有映射关系的实体也会被跟着删除。
在这里表示,如果当前 student 被删除,其 tuition 实体也会被删除,很合理。
CascadeType.MERGE
级联更新(合并)操作
当前实体中的数据改变时,会相应的更新与它有映射关系的实体的数据。
CascadeType.DETACH
级联脱管/游离操作
意思是,分离所有相关联的实体,该实体已在数据库中,对象将处于分离状态,对该对象的操作不会同步到数据库。
CascadeType.REFRESH
级联刷新操作
假设场景:有一个订单,订单里面关联了许多商品,这个订单可以被很多人操作,那么这个时候 A 对此订单和关联的商品进行了修改,与此同时,B 也进行了相同的操作,但是 B 先一步比 A 保存了数据,那么当 A 保存数据的时候,就需要先刷新订单信息及关联的商品信息后,再将订单及商品保存。
orphanRemoval = true
刚刚说到,student 表是 Parent Table,如果某个 student 没了,也就是没了爹妈,其关联的 tuition 实体就成了孤儿,变孤儿了就要被删除了。
fetch=FetchType.LAZY
懒加载,只有真正需要调用的时候才会真正从数据库里拉取数据。
JPA Fetch Types:
FetchType.LAZY
懒加载
懒加载,加载一个实体时,定义懒加载的属性不会马上从数据库中加载。
FetchType.EAGER
急加载
急加载,加载一个实体时,定义急加载的属性会立即从数据库中加载。
使用场景
比方 User 类有两个属性,name 跟 address。比如百度知道,登录后用户名是需要显示出来的,此属性用到的几率极大,要马上到数据库查,用急加载。
而用户地址大多数情况下不需要显示出来,只有在查看用户资料是才需要显示,需要用了才查数据库,用懒加载就好了。
所以,并不是一登录就把用户的所有资料都加载到对象中,于是有了这两种加载模式。
@JoinColumn(name = "student_id")
@JoinColumn
表示 tuition 这边要为这个外键关系单独加一列,名字叫 student_id
。
对比 mappedBy
来说,@JoinColumn
定义的是 owning side 实际的物理映射。而 mappedBy
定义的是 non-owning side 的引用。
经过实际测试,@JoinColumn
其实没有也行,JPA 也会为我们自动生成一列 student_id
。
id name
id fee student_id
exists
doesn't exist
YES
doesn't exist
exists
NO
exists
exists
YES