Hibernate双向一对多(多对一)

来源:互联网 发布:数据可视化原理与实践 编辑:程序博客网 时间:2024/06/05 03:53

在上一篇文章中讲解了单向多对一的关联映射,总体来讲还是很好理解的。本文将会在上一篇文章的基础上讲解双向一对多的关联映射(双向的一对多和多对一是一样的)。

上一篇文章中的一的一端的持久化类是Grade,多的一端的持久化类是Student。Student类中存在Grade的实例,即对应的数据表存在外键映射,关联Grade。但是在Grade并没有去维护这种关联关系,如何让它成为双向的呢?我们在Grade中添加一个属性,如下:

private Set<Student> students = new HashSet<Student>();

这样便引用了Student的一个集合。这里有两个注意事项,我们在后文讲解。


持久化类修改了,hbm映射文件当然也需要修改。向下面这样为Student的集合属性添加配置:

<!-- set: 映射set类型的属性,table:set中的元素对应的记录放在哪一个数据表中,该值需要和多对一的多那个表的名字一致 --><!-- cascade设置级联删除,开发时不建议使用该属性,这会导致系统的不稳定,最好使用手动方式删除 --><!-- order-by指定用来排序的列 --><set name="students" table="STUDENT" inverse="true"  ><!-- 指定多的表中的外键列的名字 --><key column="GID"></key>       <one-to-many class="Student"/></set>

上面的配置需要解释一下。由于不需要在Grade中再次生成外键(因为Grade和Student的关系已经由Student的外键维持了),所以Grade只需要维护这段关系中的一对多就可以了,所以我们需要告诉它关联表的名字,以及外键名。当然最后一步需要指定这是一对多映射并且指定集合属性的泛型(对应持久化类的类型),后面会用到的。


配置工作告一段落,来测试各种方法。

首先是save方法,测试如下:

@Testpublic void testSave(){Grade grade = new Grade("高一", "初中到高中的过渡阶段");Student student = new Student("小刁", "男");Student student2 = new Student("小碧", "女");grade.getStudents().add(student);grade.getStudents().add(student2);student.setGrade(grade);student2.setGrade(grade);// 方式一:先插入one的一端,在插入many的一端,产生三条insert语句,两条update语句session.save(grade);session.save(student);session.save(student2);// 方式二:先插入many的一端,在插入one的一端,产生三条insert语句和两条update语句,四条update语句/*session.save(student);session.save(student2);session.save(grade);*//* * 在 1-n 关系中,将 n 方设为主控方将有助于性能改善(如果要国家元首记住全国人民的名字, * 不是太可能,但要让全国人民知道国家元首,就容易的多) * 在1的配置文件中集合上添加inverse="true"即可 * 这样以上的两种方法都会减少两条update语句 */}

先说一个小问题,为了能够正常的在save方法中使用Grade的set集合类,最好的情况是在持久化类中声明的时候就完成初始化,这样可以避免不必要的错误。

接着观察打印的SQL语句。我们发现和单向多对一不同,无论先保存哪一个持久化类的对象,都会出现update,为什么呢?因为现在的关系由双方共同维护,所以无论哪一方先保存,另外一方都需要更新。有改变的方法么?有的,在刚才的<set>配置中添加一个属性inverse ,将其值设置为true。这样Grade变为被动方,这个双向关系现在由Student维护。在开发中鼓励这样做,因为在 1-n 关系中,将 n 方设为主控方将有助于性能改善(如果要国家元首记住全国人民的名字,不是太可能,但要让全国人民知道国家元首,就容易的多)。


然后再测试get方法,测试代码略过。大家可以自己试一试。然后重点看一下,使用Grade查询到对应的学生的集合的类型,我们发现返回的类型是Hibernate自带的集合类型,所以咱们又回到了上面的两个问题,第一个就是在Grade的持久化类中声明集合对象必须使用接口类型,因为Hibernate自定义的集合对象是java原生集合类型的实现。第二个是我上面说<set>的配置中配置了“泛型”,什么意思呢?其实这也是我自己的理解,因为返回的是一个集合对象,所以系统需要告诉它泛型是什么。


更新操作读者自己完成吧,没有需要讲得。


删除操作就有问题了,在不指定级联属性的情况下,什么东西你都删除不了,因为Grade和Student是相互依赖的。所以这里需要设置另外一个属性cascade,它的值倒是种类很多,讲三个常用的:delete,delete-orphan,save-update。

delete表示删除一的一端则可以将多的一端对应的也一并删除。(年级没了,学生也没了)

delete-orphan表示删除一的一端并不会真的删除一,而是删除对应的多的一端。(年级取消,年级还在,学生没了)

刚才的讲解save的代码中,Grade和Student的对象是分开来保存的。save-update则可以让系统在保存Grade对象时,一并将关联的Student的对象也保存进数据库。

但是但是,重点来了,在开发中最好不要使用cascade属性,想要删除什么直接就去数据库删除就是了。


喔,对了,还有一个属性也很常用order-by属性,如果设置了该属性, 当 Hibernate 通过 select 语句到数据库中检索集合对象时, 利用 order by 子句进行排序,order-by 属性中还可以加入 SQL 函数。

阅读全文
0 0
原创粉丝点击