Hibernate一对多映射关系详解(附带实例) 图片看不了?点击切换HTTP 返回上层
在三种关联关系中,一对多(或多对一)是最为常见的一种关联关系,一对多映射关系是由“多”的一方指向“一”的一方。在表示“多”的一方的数据表中增加一个外键,指向“一”的一方的数据表的主键,“一”的一方称为主表,而“多”的一方称为从表。
以班级和学生的关联关系为例,班级表与学生表的关联关系如图 1 所示。

图 1 班级表和学生表的关联关系
在图 1 中,grade 表为班级表,id 为班级表的主键,name 表示班级名称;student 表为学生表,id 为学生表的主键,name 表示学生名称,gid 表示外键,它对应了班级表中的主键 id。
由于一个班级可以拥有多个学生,那么可以说班级和学生是一对多的关系,在班级类中以集合 Set 的方式引入学生,在映射文件中通过 <set> 标签进行映射。
为了让读者快速掌握一对多关联关系映射的使用,下面通过具体的案例演示如何实现一对多关联关系映射。
以上代码中,定义了三个属性,分别是 id、name 和 students,并提供了各属性的 getter 和 setter 方法。其中是 students 是一个集合类型对象,用于存储一个班级的学生。
以上代码中,同样也定义了三个属性,分别是 id、name 和 grade,并提供了各属性的 getter 和 setter 方法。其中 grade 是一个 Grade 类型的对象,用于表示该学生从属于某一个班级。
上述代码中,使用 <set> 标签描述被映射类中的 Set 集合,其中 <key> 标签的 column 属性用于确定关联的外键列,<one-to-many> 标签用于描述持久化类的一对多关联,其中 class 属性表示映射的关联类。
上述代码中,<many-to-one> 标签定义了三个属性,分别是 name、class 和 column 属性。其中,name 属性表示 Student 类中的 grade 属性名称,class 属性表示指定映射的类,column 属性表示表中的外键类名。需要注意的是,该 column 属性与 Grade.hbm.xml 映射文件的 <key> 标签的 column 属性要保持一致。
上述代码中,首先创建了一个班级对象和两个学生对象,然后分别描述了两个学生与班级之间的关系,又描述了一个班级与两个学生之间的关系,这就是一对多的双向关联关系。

图 2 输出结果
从图 2 的显示结果中可以看出,控制台总共输出了三条 insert 语句和两条 update 语句,此时查询两张表中的数据,如图 3 所示。

图 3 查询结果
从图 3 查询结果中可以看出,grade 表和 student 表都已经成功创建,并插入了相应的数据。
在 Hibernate 中,像班级和学生这种关联关系,使用双向关联会执行两次 SQL 语句,这样会导致程序执行效率降低。
如果将 OneToManyTest 中的第 23 行和第 24 行代码删除,并将 grade 表和 student 表中的数据从数据库中删除,那么重新运行 test1() 方法后,控制台的输出结果如图 4 所示。

图 4 输出结果
从图 4 的显示结果中可以看出,控制台只显示出了三条 insert 语句,而没有显示 update 语句。
此时再次查询数据表中的数据,其查询结果将与图 3 中的显示结果相同。由于删除了两行描述班级有多个学生的代码,实现了班级对学生的一对多单向关联,所以程序只执行了一次 SQL 语句,但达到了同样的效果。在实际开发中,像班级和学生这种关联关系,使用单向关联描述即可。
以班级和学生的关联关系为例,班级表与学生表的关联关系如图 1 所示。

图 1 班级表和学生表的关联关系
在图 1 中,grade 表为班级表,id 为班级表的主键,name 表示班级名称;student 表为学生表,id 为学生表的主键,name 表示学生名称,gid 表示外键,它对应了班级表中的主键 id。
由于一个班级可以拥有多个学生,那么可以说班级和学生是一对多的关系,在班级类中以集合 Set 的方式引入学生,在映射文件中通过 <set> 标签进行映射。
为了让读者快速掌握一对多关联关系映射的使用,下面通过具体的案例演示如何实现一对多关联关系映射。
1. 创建项目导入 JAR 包
在 MyEclipse 中创建一个名为 hibernateDemo03 的 Web 项目,并将 Hibernate 所必需的 JAR 包添加到 WEB-INF/lib 目录中,并发布到类路径下。2. 创建实体类
1)创建班级实体类。
在项目的 src 目录下创建一个名为 com.mengma.onetomany 的包,在该包下创建一个 Grade 类,编辑后如下所示。1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | package com.mengma.onetomany; import java.util.HashSet; import java.util. Set ; public class Grade { private Integer id; private String name ; // 班级名称 private Set <Student> students = new HashSet<Student>(); public Integer getId() { return id; } public void setId( Integer id) { this.id = id; } public String getName() { return name ; } public void setName(String name ) { this. name = name ; } public Set <Student> getStudents() { return students; } public void setStudents( Set <Student> students) { this.students = students; } } |
2)创建学生实体类。
在 com.mengma.onetomany 包下创建一个名为 Student 的类,编辑后如下所示。1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | package com.mengma.onetomany; public class Student { private Integer id; private String name ; // 学生名称 private Grade grade; // 学生从属于某个班级 public Integer getId() { return id; } public void setId( Integer id) { this.id = id; } public String getName() { return name ; } public void setName(String name ) { this. name = name ; } public Grade getGrade() { return grade; } public void setGrade(Grade grade) { this.grade = grade; } } |
3. 创建映射文件
1)创建班级类的映射文件。
在 com.mengma.onetomany 包下创建一个名为 Grade.hbm.xml 的映射文件,如下所示。1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | <?xml version= "1.0" encoding= "UTF-8" ?> <!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.mengma.onetomany.Grade" table = "grade" > <id name = "id" column = "id" > <generator class= "native" /> </id> <property name = "name" column = "name" length= "40" /> <! -- 一对多的关系使用set集合映射 --> < set name = "students" > <! -- 确定关联的外键列 --> < key column = "gid" /> <! -- 映射到关联类属性 --> <one- to -many class= "com.mengma.onetomany.Student" /> </ set > </class> </hibernate-mapping> |
2)创建学生类的映射文件。
在 com.mengma.onetomany 包下创建一个名为 Student.hbm.xml 的映射文件,如下所示。1 2 3 4 5 6 7 8 9 10 11 12 13 | <?xml version= "1.0" encoding= "UTF-8" ?> <!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.mengma.onetomany.Student" table = "student" > <id name = "id" column = "id" > <generator class= "native" /> </id> <property name = "name" column = "name" length= "40" /> <! -- 多对一关系映射 --> <many- to -one name = "grade" class= "com.mengma.onetomany.Grade" ></many- to -one> </class> </hibernate-mapping> |
4. 创建配置文件和工具类
在 src 目录下创建一个名为 hibernate.cfg.xml 的配置文件,在该文件行中配置数据库基本信息和其他配置信息后,将 Grade.hbm.xml 和 Student.hbm.xml 映射文件添加到配置文件中,具体如下所示:
<mapping resource="com/mengma/onetomany/Grade.hbm.xml" />
<mapping resource="com/mengma/onetomany/Student.hbm.xml" />
5. 创建测试类
在 com.mengma.onetomany 包下创建一个名为 OneToManyTest 的类,编辑后如下所示。1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | // 添加数据 @Test public void test1() { Session session = HibernateUtils.getSession(); session.beginTransaction(); // 创建一个班级 Grade g = new Grade(); // 创建两个学生对象 g.setName( "一班" ); Student s1 = new Student(); s1.setName( "张三" ); Student s2 = new Student(); s2.setName( "李四" ); // 描述关系 ---学生属于某个班级 s1.setGrade(g); s1.setGrade(g); // 描述关系 ---班级里有多个学生 g.getStudents(). add (s1); g.getStudents(). add (s2); // 先存班级再存学生 session.save(g); session.save(s1); session.save(s2); session.getTransaction(). commit (); session. close (); } |
6. 运行项目并查看结果
在 Hibernate 配置文件中添加自动建表信息后,在运行程序时,程序会自动创建两张表,并且插入数据。使用 JUnit 测试运行 test1() 方法后,控制台显示结果如图 2 所示。
图 2 输出结果
从图 2 的显示结果中可以看出,控制台总共输出了三条 insert 语句和两条 update 语句,此时查询两张表中的数据,如图 3 所示。

图 3 查询结果
从图 3 查询结果中可以看出,grade 表和 student 表都已经成功创建,并插入了相应的数据。
在 Hibernate 中,像班级和学生这种关联关系,使用双向关联会执行两次 SQL 语句,这样会导致程序执行效率降低。
如果将 OneToManyTest 中的第 23 行和第 24 行代码删除,并将 grade 表和 student 表中的数据从数据库中删除,那么重新运行 test1() 方法后,控制台的输出结果如图 4 所示。

图 4 输出结果
从图 4 的显示结果中可以看出,控制台只显示出了三条 insert 语句,而没有显示 update 语句。
此时再次查询数据表中的数据,其查询结果将与图 3 中的显示结果相同。由于删除了两行描述班级有多个学生的代码,实现了班级对学生的一对多单向关联,所以程序只执行了一次 SQL 语句,但达到了同样的效果。在实际开发中,像班级和学生这种关联关系,使用单向关联描述即可。