@Table(name = "wife")
public class Wife (){
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
//注意:由于是双向的一对一关系。作为关系的被维护端。需要在wife实体中添加和 husband 关联的属性
//mappedBy 注释:指定了,这个实体是被关系维护端的那个属性所维护。
//在本例中,Wife实体是被 Hhusband实体中的外键“wife”所维护。
@OneToOne(mappedBy="wife")
private Husband husband;
//省略 get / set 方法...
}
那么,现在已经很明显了。在Wife 的 Model 中,已经找到了与 Husband 关联的属性了。但是,如果进入到数据库中查看Wife这张表,你并不会在表里找到关联到Husband的外键。为什么会这样呢?因为,我们定义关系的时候,规定了关系的维护端是Husband。所以,只有Husband中有管理到Wife的外键。而这个一对一关系,就是靠这个外键来维护的。那当我们设置了,mappedBy后,使关系成为双向的时候,是怎样通过Wife实体来得到她对应的Husband实体的呢?这都是由JPA来做的。我们不必在这上面花费过多的精力。如果,你感兴趣,也可以自己研究思考一下。
-----------------------------------------测试--------------------------------------------------------------------------
测试方法:
//省略包的引用:HusbandDao , WifeDao
public class TestOneToOneEitherSide() {
//测试方法1-------创建实体:丈夫 和 妻子
public void testCreate() {
Husband wang = new Husbnad();
wang.setName("老王");
Wife li = new Wife();
li.setName("莉莉");
//由于关系是双向的,所以,你不必手动的来设置“莉莉”的丈夫。JPA会自动完成这个操作。所以,即使将这个操作注释掉,双向关系还是会正确的建立的。
//wife.setHusband(wang);
wifeDao.save(li);
//将“莉莉” 交给 “老王”。
//但是,这一步,就不可以省略了。必须得手动的或者说显式的设置“老王”和“莉莉”的关系。如果,你省略了这一步,那么就没法建立起 “老王” 和 “莉莉” 的你一对一关系了,无论是双向的还是单向的。
wang.setWife(li);
//此时,老王 和 莉莉 ,已经是双向的一对一关系了。通过他俩任何一方,都可以找到另一个对象。
husbandDao.save(wang);
//验证方法1:
//在表 Husband 中找到名字是老王的丈夫。
Husband husband = husbandDao.findByName("老王");
//通过老王得到他的妻子实体,并调用妻子的getName()方法,得到老王妻子的姓名。
Wife wangWife = husband.getWife();
String wifeName = wangWife.getName();
System.out.println(wifeName);//结果为: 莉莉
//验证方法2:与方法1类似,简写。
Wife wife = wifeDao.findByName("莉莉");
String husbandName = wife.getHusband().getName();
System.out.println(husbandName);//结果为: 老王
//通过这两个验证方法,我们已经知道,在设置了双向一对一关系后。我们已经可以通过关系的一方,得到与之对应的另一方了。其实,双向的实际使用意义也正是如此。通过关系的任意一方可以得到与之对应的另一方
}
//测试方法2-------删除实体:丈夫
public void testDeleteHusband() {
//在表 Husband 中找到名字是老王的丈夫。
Husband wang = husbandDao.findByName("老王");
//校验空值和空对象的方法省略,因为肯定会得到老王这个实体的。
husbandDao.delete(wang);
//验证方法1:
//在表 Husband 中找到名字是老王的丈夫。
Husband husband = husbandDao.findByName("老王");
System.out.println(husband);//结果为: 空(null)
//验证方法2:与方法1类似,简写。
Wife wife = wifeDao.findByName("莉莉");
if(wife != null){
Husband wifeHusband = wife.getHusband();
System.out.println(wifeHusband);//结果为: 空(null)
}
//名字叫“老王”的丈夫已经被顺利的删除掉了。没有报异常,他的妻子“莉莉”并没有被一同删掉。(因为没有添加级联删除的原因)。此时,我们发现“莉莉”的丈夫属性也是空的。老王这个关系的维护端被删除的时候,JPA会做相应的操作,并不会影响到“莉莉”。表Wife中本来也没有与Husband 关联的外键,删除 老王,当然不会对 莉莉 产生影响了。因为她的丈夫属性上有mappedBy 注释。
}
//测试方法3-------直接删除实体:妻子
//这种直接删除的方式,会产生异常
public void testDeleteWife() {
//得到名字是“莉莉”的实体
Wife li = wifeDao.findByName("莉莉");
wifeDao.delete(li);
//此时后台会报异常,数据库回滚,无法删除"莉莉"
}
-------------------------------------------------------------------------------------------------------------------
假设,数据库没有回滚,那么莉莉删除掉后。在表 Husband 中,老王这个实体的 外键 还是 "莉莉" 的id。但是,此时,莉莉已经被删除掉了。这样当然会产生异常。在上一篇帮助手册【JPA】 @OneToOne 单向 中,我们曾经遇到过这种情况。删除关系被维护端之前,没有从关系维护端手动解除关系,后台就一定会报异常。在任何关系中,都是这样,不论他是单向的还是双向的,不论是一对一、一对多、多对多。只要想删除关系的被维护端,必须先从关系的维护端手动解除关系。
在看正确的删除被维护端方法前,我们应该回忆一下,在【JPA】 @OneToOne 单向 中,删除关系被维护端是一件多么痛苦的事儿啊。我得通过循环去 关系维护端所在的表中,找到要删除的实体对应的维护端。但是,在双向的 一对一 关系中。找到实体的维护端,好像并不是一件痛苦的事儿。
-------------------------------------------------------------------------------------------------------------------
//测试方法4-------解除关系并删除实体:妻子
//正确的删除方法
public void rightDeleteWife(){
//得到名字是“莉莉”的实体
Wife li = wifeDao.findByName("莉莉");
//得到她的关系维护端,也就是她的丈夫
Husband liHusband = li.getHusband();
//此时,我们要强迫 “老王” 解除 他和 “莉莉” 的关系
liHusband.setWife(null);
wifeDao.delete(li);
//验证方法:
Husaband wang = husbandDao.findByName("老王");
Wife wangWife = wang.getWife();
//当存在老王这个实体,并且老王没有妻子时,打印我们的校验信息
if(wang != null && wangWife == null){
System.out.println("老王单身了");
}
//果然,这句话被打印出来了。莉莉 被成功的删除掉了,老王的外键也被置空了。看来,想要删除被维护端,只能手动的先从关系维护端解除关系再删除。
}
===============================================================
至此,一对一关系,已经向大家全部介绍完毕了。其实,建立一对一关系,还有别的方法。比如共享主键之类的。但是,用这两篇文章的方法建立一对一关系比较方便,也不会产生第三张码表。数据库比较干净。如果,大家对另外两种建立方法感兴趣,可以自己查看我们翻译的中文API。这里就不做阐述了。
这两篇文章,都强调了关系被维护端的删除操作。就是想让大家重视,关系的维护端的作用和删除操作易发生的异常。至于级联操作,由于大家刚刚熟悉关系,所以,稍后会在 @OneToMany(一对多) 关系中,向大家慢慢阐述。而且,级联操作,在一对多关系中的作用比较大。但是,一定要慎用,否则会产生你意料不到的麻烦。请期待【JPA 级联保存/级联删除】@OneToMany 一对多(单向和双向)注解