您的位置:首页 > 其它

千山万水之Hibernate(六)——关联映射(一对多)

2015-05-31 14:03 316 查看
学习Hibernate的过程中,映射是比较重要的一部分,再说其中哪一种映射比较重要(或可以说是比较常用)呢?那一定很多人会想到一对多关联映射。之所以这样说,是因为在生活中很多这样的实例:班级-学生、企业-员工、文件夹-文件、试卷-题目等。至于生活中为什么会遇到这样大量一对多的情况,似乎是哲学方面的事情了,当然大家可以继续思考,而我们今天主要讨论Hibernate中的一对多。

原理分析

我们仍然拿班级、学生之间的关系做例子:一个班级包含多个学生。相应的实体关系图为:



单向关联

由图可知,由于单向的引用,需要Classes来维护班级、学生二者之间的关系,也就是说,当对Student进行增、删、改时,需要Classes来发起之间关系的维护。另一方面,体现到数据库层面上,我们可以想象,怎样可以实现两个表之间的关系呢?这时候我们可以想到之前练习过的多对一的情况。实际上在实现原理方面,这里所说的一对多和多对一是一样的,都需要在多的一端加入一的一端的外键。

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="com.tgb.hibernate.Classes" table="t_classes">
<id name="id">
<generator class="native"></generator>
</id>
<property name="name" />
<!--这里采用set标签代表多端,在多端表添加外键列,由一的一端维护此外键关系-->
<set name="students">
<key column="classesId" />
<one-to-many class="com.tgb.hibernate.Student" />
</set>
</class>
</hibernate-mapping>


操作示例

测试程序如下:

public void testSave2(){
Session session = null;
Transaction tx = null;
try{
session = HibernateUtils.getSession();
tx = session.beginTransaction();

Student student1 = new Student();
student1.setName("李雷");
session.save(student1);

Student student2 = new Student();
student2.setName("韩梅梅");
session.save(student2);

Set<Student> students = new HashSet<Student>();
students.add(student1);
students.add(student2);

Classes classes = new Classes();
classes.setName("TGB——9");
classes.setStudents(students);
session.save(classes);

tx.commit();
}catch(Exception e){
e.printStackTrace();
if(tx != null){
tx.rollback();
}
}finally{
HibernateUtils.closeSession(session);
}
}


(说明:程序中调用HibernateUtils的代码实现可参考:千山万水之Hibernate(四)——关联映射(多对一)

执行打印SQL语句如下:

Hibernate: insert into t_students (name) values (?)
Hibernate: insert into t_students (name) values (?)
Hibernate: insert into t_classes (name) values (?)
Hibernate: update t_students set classesId=? where id=?
Hibernate: update t_students set classesId=? where id=?


可以看到,在执行了保存学生、保存班级的SQL语句后,在提交事务时,又发出了更新外键关系的SQL语句,也就是说,在调用session.save(student)的时候,student端不知道classes一端的存在,不能将关联字段添加上。这样出现的问题有两个:

在提交事务、清理缓存时,会发出额外的update语句,更新关系;

如果上面的问题还不至于太严重的后果,那在火上浇油一把:



外键列设置为非空,那将会在执行的过程中报错:

ERROR JDBCExceptionReporter:72 - Field 'classesId' doesn't have a default value


问题解决:

设置一对多为双向关联,将维护关系端设置为多端,一端不再维护关系,这样在添加学生的时候就将关系直接保存,而对于一端的加载,不能构成影响。

设置一端配置控制方向上的反转为true,这样只影响数据的存储,不会影响加载。

总结

我们会说,设置关联上的映射关系,通常会根据业务需求来设计,但这里的双向关联,主要是解决一对多单向关联的缺陷,而不是需求驱动的。因此我们的设计也应该灵活,不能严守教条,既要结合具体的业务,也要考虑技术的实现程度。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: