原文请点击此处
@OneToOne 定义:一对一关系。
生活中的一对一关系,举例:人(man) 和 宠物(pet)。前提(一人只养一个宠物)
为什么这个一对一关系是单向的?如果,人养了宠物,那么我们通过“人”就能得到他所拥有的“宠物”的实体。但是,是不是通过“宠物”就能得到“人”的实体呢?!恐怕未必吧~因为在实际生活中,有很多走失的宠物,我们无法通过它们找到它们的主人。
类似于上述这种情况,或者业务关系。实体间的关系是一对一,并且,我们只需要通过一个实体得到其对应的实体,而且并不需要反向执行这个操作的时候。我们就需要使用单向一对一关系。
例子:
@Entity
@Table(name = "people")
public class People (){
@Id //JPA注释: 主键
@GeneratedValue(strategy = GenerationType.AUTO) //设置 id 为自增长
private Long id;
private String name;
//由于,people 是这个一对一的关系的主控方,所以,在people表中添加了一个 pet 的外键。
//通过这个外键来维护 people和pet的一对一关系,而不是用第三张码表。这个是通过@JoinColumn注释实现的。
@OneToOne //JPA注释: 一对一 关系
@JoinColumn(name="pet_fk" )// 在pepole中,添加一个外键 "pet_fk"
private Pet pet;
//省略 get / set 方法...
}
@Table(name = "pet")
public class Pet (){
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
//省略 get / set 方法...
//因为这是一个单向的一对一关系,并且,是从 people 到 pet 的一对一关系。
//所以,在 pet 中没有与 people 管理的 属性。也就是说,无法通过 pet 找到 people
}
===========================测试====================================
//省略包的引用... PeopleDao , PetDao
public class TestSingleSideOneToOne (){
//测试方法 创建----实体(人&宠物)
public void testCreate(){
//创建人
People people = new People();
people.setName("人");
//创建宠物
Pet bird = new Pet();
bird.setName("鸟");
petDao.save(bird);
Pet cat = new Pet();
cat.setName("猫");
petDao.save(cat);
Pet dog = new Pet();
dog.setName("狗");
petDao.save(dog);
//此时,有了人,也有了宠物,我们终于可以让这个人领养他的宠物了。
//就让他领养一只小狗吧
people.setPet(dog);
peopleDao.save(people);//保存people实体。
//让我们来验证一下
People testPeople = peopleDao.findByName("人");
//得到这个人养的宠物,并调用宠物get方法,得到宠物的名字
String petName = testPeople.getPet().getName();
System.out.println(petName);//打印的结果必然是 “狗”
}
//测试方法2 删除--- 实体(人)
public void testDeletePeople(){
//得到名字叫“人”的 人 的 实体
People people = peopleDao.findByName("人");
//删除实体(人)
peopleDao.delete(people);
//在方法执行过后,人这个实体被从数据库中删除了。但是,宠物的实体还存在,并没有受到任何的影响。因为,people 和 pet 是单向的一对一关系。删除人,当然不会对宠物实体造成任何影响。那么,如果我们想要删除宠物,结果还会是一样的么?请看下一个测试方法。
}
//测试方法3 删除----实体(宠物:鸟)
public void testDeleteBird(){
//得到名字叫“鸟”的 宠物 的 实体
Pet pet = petDao.findByName("鸟");
//删除实体(宠物:鸟)
petDao.delete(pet);
//验证方法
List pets = petDao.findAll();//得到全部的宠物实体集合
if(pet != null){
Pet pet = pets.get(i);//得到当前循环的宠物实体
System.out.println(pet.getName());//打印宠物的名字
}
}
//当方法执行结束后,你会看到 "狗" “猫” 被打印出来了。而,“鸟”的实体,已经从 表pet中被删除掉了。是不是删除每一只宠物都如此简单顺利呢?恐怕未必吧。还记得那句话么? “打狗也得看主人啊~”
}
//测试方法4 删除----实体(宠物:狗)
//这个方法执行后,会产生异常
public void testDeleteDog(){
//得到名字叫“狗”的 宠物 的 实体
Pet pet = petDao.findByName("狗");
//删除实体(宠物:狗)
petDao.delete(pet);
//为什么会产生异常呢?因为你只是删除了宠物这个实体,但是却没有解除关系(在people表中的外键pte_fk 还是 “狗”)。JPA不会自动的去解除关系。这是缺点,也是它的有点。可以在正常删除的时候,通过捕获异常,来达到一些业务操作。就像刚刚这种情况,在你想要删除 宠物的时候,发生了异常,就说明 有人正在饲养这只小狗。这不是被遗弃的宠物,你无权处理它。除非,它被抛弃了(关系维护端,人,解除了关系。人 实体中的pet 属性置空,关系解除。)
//那么让我们继续往下看,看看如何正确的,删除掉宠物实体。
}
//测试方法4 删除----实体(宠物:狗)
//在方法开始前,我会告诉你。在一对一单向关系中,没有添加级联删除操作。并且还想删除 被维护端。这是一个多么不理智的行为啊。那么,随后,你就知道我为什么会这么说了。
public void testDeleteDog2(){
//得到名字叫“狗”的 宠物 的 实体
Pet dog = petDao.findByName("狗");
//首先,我们要得到全部人的集合
List peoList = peopleDao.findAll();
//省略空值校验
People peo = peoList.get(i);
//看看谁养了这只小狗。我们要强迫他抛弃自己的小狗。
if(peo.getPet() == dog){
//让他抛弃他的小狗。(从维护端解除关系)
peo.setPet(null);
//为什么保存“人”实体的操作会被注释掉呢?在置空操作后,不需要保存么?这就是JPA。只要你关系设置的正确,许多操作它都会替你完成。当小狗被删除后,小狗主人的对于属性已经是置空状态了。
//peopleDao.save(peo);
return null;
}
}
//删除实体(宠物:狗)
petDao.delete(dog);//至此,那个可怜的主人,如果你想的话,你还可以重新领养一只“猫”。
//省略验证方法
//小狗被成功删除
}
}
在进行了,循环查找,解除关系的操作后,我们终于把这只小狗从 表pet 中删除了。但是,为了删除这只狗,我们都付出了什么。一个循环操作查找谁养了这只宠物的操作,浪费了多少资源和性能?
之所以在删除这里,用了这么大的篇幅,就是想向大家强调,选择关系的重要性!!!如果,这是一个双向的一对一关系。那么,我可以直接通过这只小狗找到它的主人,然后让它的主人解除关系。随后就可以轻松的把它删除掉。他的主人随后也可以继续领养其他的宠物。不需要通过繁琐的循环了查找它的主人。
通常情况下,采用 单向一对一关系,会在关系维护端添加上级联删除操作。删除主人的时候,自动删除主人领养的宠物实体。我们不关心宠物实体的维护。它的存在和消亡,应该依赖于自己的主人。当主人不消亡的时候,它也失去了存在的意义。
对于级联操作和其他关系类型。我会再随后的文章里,继续想大家介绍。另外,对于这个不是特别恰当,且冷血的例子,请青少年朋友们,在成人的陪同下一起学习。我们应该热爱小动物。他们应该是双向的,相互关联的,那么,就让我们一起期待【JPA】@OneToOne 一对一双向关联注解