一.Hibernate一对多配置时两个常见的关键属性。
在Hibernate的一对多配置时,有两个常见的关键属性,inverse和cascade. 两个属性要表达的意思是不一样的。 下面,分别进行相关的说明。 其中,所用的例子,是第九章的例子。 Dept与User. 部门与员工的一对多例子。
二.cascade的属性
cascade,级联的意思。就是在一对多的过程中,对一的操作,会对多的那一方产生什么样的影响。 其中,在数据库知识里,表现在对外键的那个字段上面。常见的就是,删除一时,如果在多的那一方,即删除部门时,如果在员工那张表里面有相应的属性,是不能进行删除的。 在添加的时候,如果设置外键不能为空,那么添加员工的时候,是不能添加没有部门的员工记录,或者部门不存在的员工记录。Hibernate对这些进行了扩展,用一个cascade属性来进行相互的级联操作。
其中,cascade有五个值,
1.none 添加修改删除时,不考虑其他的操作。 是默认值。
2.save-update 添加和修改的时候,级联考虑附属物。
3.delete 删除时,级联考虑附属物
4.all 添加,修改删除时均考虑附属物。 为save-update+delete
5.delete-orphan(孤儿): 删除时,删除和当前对象解除关系的附属物。
6.all-delete-orphan: all+delete-orphan
这些设置,是在一的一方进行设置,且设置在set元素上。
二.一 默认的操作 cascade=“none”
@Test public void noneTest(){ /*1。通过工具类构建Session*/ Session session=HibernateUtil.getSession(); Dept dept=new Dept(); dept.setName("开发部"); dept.setDescription("一切为了开发"); /*3. 实例化User类,并设置与部门的关系*/ User user=new User(); user.setName("两个蝴蝶飞"); user.setSex("男"); user.setAge(24); user.setDescription("一个有梦想的程序员"); User user1=new User(); user1.setName("两个蝴蝶飞1"); user1.setSex("男"); user1.setAge(24); user1.setDescription("一个有梦想的程序员"); User user2=new User(); user2.setName("两个蝴蝶飞2"); user2.setSex("男"); user2.setAge(24); user2.setDescription("一个有梦想的程序员"); /*设置与部门的关系*/ user.setDept(dept); user1.setDept(dept); user2.setDept(dept); /*设置部门与员工的关系*/ dept.getUsers().add(user); dept.getUsers().add(user1); dept.getUsers().add(user2); /*如果按照以前的方式添加,那么对dept,user,user1,user2分别进行保存。 * 现在只保存dept,看效果如何。*/ session.save(dept); //session.save(user);session.save(user1);session.save(user2); //省略不保存。 }
只插入部门记录。没有插入员工的记录。
当cascade=“none”, 这个时候,只会保存dept,也就是主控操作,并不会保存user的信息,也就是关联操作。
这个时候,突发设想,如果保存多的一方会怎么样,即只保存session.save(user); 是什么情况。 将记录删除,改写代码,重新运行。
只是修改外键,添加user的记录。并不会添加dept的记录。
二.二 cascade=“save-update” 添加的操作。
- 在Dept.hbm.xml文件中,将cascade=“none” 改成cascade=“save-update”
2.编写添加的测试
@Test public void addTest(){ /*1。通过工具类构建Session*/ Session session=HibernateUtil.getSession(); Dept dept=new Dept(); dept.setName("开发部"); dept.setDescription("一切为了开发"); /*3. 实例化User类,并设置与部门的关系*/ User user=new User(); user.setName("两个蝴蝶飞"); user.setSex("男"); user.setAge(24); user.setDescription("一个有梦想的程序员"); User user1=new User(); user1.setName("两个蝴蝶飞1"); user1.setSex("男"); user1.setAge(24); user1.setDescription("一个有梦想的程序员"); User user2=new User(); user2.setName("两个蝴蝶飞2"); user2.setSex("男"); user2.setAge(24); user2.setDescription("一个有梦想的程序员"); /*设置与部门的关系*/ user.setDept(dept); user1.setDept(dept); user2.setDept(dept); /*设置部门与员工的关系*/ dept.getUsers().add(user); dept.getUsers().add(user1); dept.getUsers().add(user2); /*如果按照以前的方式添加,那么对dept,user,user1,user2分别进行保存。 * 现在只保存dept,看效果如何。*/ session.save(dept); }
2.运行之后的顺序是:1,先修改user表,添加外键。 2 插入dept表。 3 在user表中插入user记录。 4. 在user表中插入user1记录。 5. 插入user2记录。
这个时候,只需要保存dept对象即可。 后台会根据cascade属性,自动将它的关联操作三个user对象进行相应的保存。
3.突发奇想,只保存session.save(user);会是什么样的呢? 不保存dept表。 结果是,1,先修改user表的外键。2 后插入user对象记录到user表中。并不会关联Dept表。其中dept外键为null. 所以,cascade 只是针对一的那一方进行的。对主控一方才有效。
二.三 级联修改的测试
修改时,cascade的值仍然是"save-update",不用进行修改。
@Test public void updateTest(){ /*1。通过工具类构建Session*/ Session session=HibernateUtil.getSession(); //修改的时候,不要忘记添加事务。 Transaction transaction=session.beginTransaction(); transaction.begin(); Dept dept=session.get(Dept.class,1); //2. 改变部门的值 dept.setName("测试级联保存"); //3. 取出部门下的员工,改变员工的值。 Set<User> userList=dept.getUsers(); for (User user : userList) { user.setDescription("测试级联保存员工的值"); } //4. 设置级联保存 session.update(dept); //5.以前要将userList中的每一个对象取出,然后更新每一个对象。 在foreach循环中: session.update(user); transaction.commit(); }
执行的顺序是:
1.修改user表的外键。
2.根据dept的编号1,查部门的记录。
3.根据部门的编号,去user表中查询员工的相关信息。
4.修改部门的记录。
5.根据user员工的编号,修改user对象的记录。
6.根据user1员工的编号,修改user1对象的记录。
7.根据user2员工的编号,修改user2对象的记录。
以前的话,就得分别进行保存user,user1,user2. 现在直接保存dept对象即可。Hibernate会自动帮助我们级联保存关联对象。
二.四 cascade=“delete”, 级联删除的测试
先将cascade=“save-update” 改成cascade=“none”,只测试一下,原先的,即默认的删除情况。
@Test public void oldDeleteTest(){ /*1。通过工具类构建Session*/ Session session=HibernateUtil.getSession(); Transaction transaction=session.beginTransaction(); transaction.begin(); Dept dept=session.get(Dept.class,1); //4. 设置普通的删除 session.delete(dept); transaction.commit(); }
这个时候,执行的操作是:
- 修改user表的外键信息
- 根据部门编号,去查部门的信息
- 更新user表,令外键信息为该部门编号的,设置成null
- 删除delete dept表。
注意这时,将cascade=“none” 改成 cascade=“delete” ,代码与上面的普通删除一样。
@Test public void DeleteTest(){ /*1。通过工具类构建Session*/ Session session=HibernateUtil.getSession(); Transaction transaction=session.beginTransaction(); transaction.begin(); Dept dept=session.get(Dept.class,1); //4. 设置普通的删除 session.delete(dept); transaction.commit(); }
1.修改user表的外键信息
2.根据部门编号去查询部门
3.根据部门编号外键去user表中查询相关的信息
4.将user表中的部门外键设置成null
5.删除user表中的user对象
6.删除user表中的user1对象
7.删除user表中的user2对象
8.删除dept表中的dept对象
二.五 cascade=“all” 的测试
在cascade=“save_update” 时,只是对插入和修改进行了级联,当删除时,并不会发生级联的操作。 在cascade="delete"时,只是对删除进行了级联,并不会对插入和修改进行级联。 只有在设置cascade=“all” 时,才会对插入,修改,删除进行级联。 其中,cascade=“all” 也可以写成 cascade=“save-update,delete” . 具体设置成什么值,还是看具体的业务分析,看级联分析。 这个不进行相应的测试了。
二.六 cascade=“delete-orphan” 的测试
这个orphan,是孤儿的意思,就是只剩下它一个的意思。 这个利用代码说明一下吧。
1.将cascade=“all” 改成cascade=“delete-orphan”
2.代码编号如下:
@Test public void orphanTest(){ /*1。通过工具类构建Session*/ Session session=HibernateUtil.getSession(); Transaction transaction=session.beginTransaction(); transaction.begin(); Dept dept=session.get(Dept.class,4); //2. 改变部门的值 dept.setName("测试孤儿保存"); //3. 取出部门下的员工,改变员工的值。 Set<User> userList=dept.getUsers(); Iterator<User> iterator=userList.iterator(); while(iterator.hasNext()) { User user=iterator.next(); user.setDescription("测试级联保存员工的值"); if(user.getId()==10){ user.setDept(null); //解除关系 iterator.remove(); //将那个值进行删除。 } } //4. 设置孤儿更新 session.update(dept); transaction.commit(); }
这个时候执行的操作是:
1.修改user表的外键信息
2.根据部门编号查询部门信息
3.根据user表的外键部门信息查询员工的信息
4.更新dept的信息
5.更新user1的信息
6.更新user2的信息
7.将user的信息的外键设置为Null
8.删除user的信息
其中,要说明的一点, 并不是先更新user的信息,再将user的外键设置成Null. 而是直接设置成null,进行删除。 这里实际上用到了一级缓存的问题。
这个的意思是: orphan,孤儿,即失去了联系,也就是解除了关系,id=10的那条记录,与dept解除了关系,也就是id=10的那条记录成了孤儿,将其进行删除。
实际上,delete-orphan 做了三个工作:
1.save-update 的工作。 级联添加和保存 用save,或者update,saveOrUpdate()方法时。
2.delete的工作 级联删除 用delete()方法
3.删除解除关系的那个对象。 有几个,解决几个,并不是仅仅解除一个.
二.七 cascade=“all-delete-orphan” 的测试
实际上就是 all+delete-orphan 。 与上面的代码基本是一样的。
常用的仅仅是:save-update,delete,all 三种而已。
三 inverse 属性
Hibernate在操作一对多的时候,是默认那个外键是双向关联的。 即双方共同维护。 Dept表维护这个外键,User表也维护这个外键。 在数据库操作的时候,就是数据库层面的时候,只是由User表进行相应的维护,就是多的那一方进行维护。 有 inverse的属性来进行判断和决定。
默认是false,即双方共同维护。 这个也是设置在一的那一方。 inverse=true. 即一的那一方放弃维护外键。
此时,在Dept.hbm.xml文件中,将cascade=“save-update”,将inverse=“false” 即默认的。
那么在进行测试时:
/*1。通过工具类构建Session*/ Session session=HibernateUtil.getSession(); Transaction transaction=session.beginTransaction(); transaction.begin(); //2. 获取已经存在的部门 Dept dept=session.get(Dept.class,6); //3.获取已经 存在的员工 User user=session.get(User.class,16); //4.设置关联 dept.getUsers().add(user); user.setDept(dept); //4. 设置孤儿更新 session.update(dept); transaction.commit();
维护外键的时候,设置了两次。 其中,在Hibernate5时,只设置一条:
dept.getUsers().add(user); 不用设置user.setDept(dept); 也可以达到关联的目的。 此时,外键只设置一次。 但是如果是两行代码,那么 此时,外键设置了两次。 浪费资源。
应该将inverse改成: inverse=“true” . 改成true之后,再次执行的话:
只有这一次设置。
四. cascade和inverse属性的注意点
我看网上有说,分析cascade和inverse的区别之类的,实际上,两者根本不是一个关系,说得不是一码事,哪有什么区别之说。
1.cascade 说得是操作一个对象是,是否操作它的关联对象
2.inverse 强调的是外键的维护权由哪一方来维护
根本就不是一码事,好不好。应该要特别注意。
3.其中,级联时,可以级联添加和修改,最好不要级别删除。 即cascade=“save-update” 要常用,cascade=“delete” 要慎用。 cascade=“delete-orphan” 不要勿用。
4.inverse时,一对多的时候,要一的一方放弃维护,inverse=true. 由多的一方进行维护。 多对多的时候,要一方放弃维护,或者双方均放弃维护。(因为多对多的时候,我建议设置成两个一对多的形式).
谢谢!!!