Hibernate的关联映射之1-N关联映射

三、1—N

对于1-N而言,它的持久化类发生了一点改变,持久化类里需要使用集合属性。因为1的一端需要访问N的一端,而N的一端将以集合(Set)形式表现。

1、单向1-N关联

对于单向的1-N关联关系,只需要在1的一端增加Set类型的属性,该属性记录当前实体的关联实体。

同样以员工-部门为例(Employee-->Department)。两个持久化类如下:

Department

[java] view
plain
 copy

print?

  1. public class Department {
  2. private Integer id;
  3. private String name;
  4. private Set<Employee> employees;          //关联关系
  5. public Integer getId() {
  6. return id;
  7. }
  8. public void setId(Integer id) {
  9. this.id = id;
  10. }
  11. public String getName() {
  12. return name;
  13. }
  14. public void setName(String name) {
  15. this.name = name;
  16. }
  17. public Set<Employee> getEmployees() {
  18. return employees;
  19. }
  20. public void setEmployees(Set<Employee> employees) {
  21. this.employees = employees;
  22. }
  23. }

Employee

[java] view
plain
 copy

print?

  1. public class Employee {
  2. private Integer id;
  3. private String name;
  4. public Integer getId() {
  5. return id;
  6. }
  7. public void setId(Integer id) {
  8. this.id = id;
  9. }
  10. public String getName() {
  11. return name;
  12. }
  13. public void setName(String name) {
  14. this.name = name;
  15. }
  16. }

1.1基于无连接表的单向1-N关联

对于1-N的单向关联,需要在1的一端增加对应的集合映射元素,如<set.../>、<bag.../>。在集合元素中需要增加<key.../>子元素,该子元素用来映射外键。同时集合元素中需要使用<one-to-many.../>元素来映射1-N关联关系。

下面是Department的映射文件Department.hbm.xml

[html] view
plain
 copy

print?

  1. <hibernate-mapping package="com.hibernate.domain" >
  2. <class name="Department" table="department">
  3. <id name="id" column="departmentID">
  4. <generator class="native" />
  5. </id>
  6. <property name="name" column="departmentName" />
  7. <!-- 映射集合属合 -->
  8. <set name="employees" inverse="true" >
  9. <!-- 指定关联的外键列 -->
  10. <key column="departmentID" />
  11. <!-- 用以映射到关联类属性 -->
  12. <one-to-many class="Employee"/>
  13. </set>
  14. </class>
  15. </hibernate-mapping>

对于上面的映射文件,映射<set.../>元素时并没有指定cascade属性,在默认的情况下,对主表实体的持久化操作不会级联到从表实体。

Employee.hbm.xml

[html] view
plain
 copy

print?

  1. <hibernate-mapping package="com.hibernate.domain">
  2. <class name="Employee" table="employee">
  3. <id name="id" column="employeeID">
  4. <generator class="native" />
  5. </id>
  6. <property name="name" column="employeeName" />
  7. </class>
  8. </hibernate-mapping>

使用下面代码来操作Department和Employee实体:保存一个Department实体和两个Employee实体

[java] view
plain
 copy

print?

  1. static void add(){
  2. Session session = HibernateUtil.getSession();
  3. Transaction tx = session.beginTransaction();
  4. Department department = new Department();
  5. department.setName("国防部");
  6. //建立两个对象
  7. Employee employee1 = new Employee();
  8. employee1.setName("chentmt1");
  9. Employee employee2 = new Employee();
  10. employee2.setName("chentmt2");
  11. Set<Employee> emps = new HashSet<Employee>();
  12. emps.add(employee1);
  13. emps.add(employee2);
  14. //设置Department和Employee之间的关联关系
  15. department.setEmployees(emps);
  16. session.save(department);          //....1
  17. session.save(employee2);
  18. session.save(employee1);
  19. tx.commit();
  20. session.close();
  21. }

分析上面代码段:

当程序运行到....1的时候,系统会持久化该对象:Department,而且这个对象已经关联了两个Employee对象。在这个时候Hibernate需要完成两件事:

1、执行insert语句想department表中插入一条记录

2、Hibernate试图执行update语句,将当前的department表记录关联的employee表记录的外键departmentID修改为该department记录的主键的值。

下面为上面代码段的sql语句:

[sql] view
plain
 copy

print?

  1. Hibernate: insert into department (departmentName) values (?)
  2. Hibernate: insert into employee (employeeName) values (?)
  3. Hibernate: insert into employee (employeeName) values (?)
  4. Hibernate: update employee set departmentID=? where employeeID=?
  5. Hibernate: update employee set departmentID=? where employeeID=?

从上面的sql语句中我们可以看到虽然程序仅仅需要为Department实体增加一个关联Employee实体,但是Hibernate会采用两条SQL语句来完成:一条inset语句插入一个条外键为null的employee记录,一条update语句修改插入的employee记录。造成这个问题的根本原因就在于:从Department到Employee的关联并没有被当中为Department对象状态的一部分(程序是通过吧Employee实体添加到Department实体的集合属性中,而Employee实体并不知道她所关联的Department实体),因而Hibernate无法在执行insert语句为该外键指定值。

为了解决这个问题,程序必须在持久化Employee实体之前,让Employee实体能够知道它所关联的Department实体,也就是通过employee.setDepartment(department);方法建立关联关系,但是这个时候就已经变成了1-N的双向关联。所以,尽量少用1-N的单向关联,而改用1-N的双向关联。

1.2基于有连接表的单向1-N关联

对于有连接表的1-N关联映射,映射文件不再使用<one-to-many..../>元素映射关联实体,而是使用<many-to-many.../>元素,但是为了保证当前实体是1的一端,我们需要增加一个属性:unique="true"。

所以Department.hbm.xml配置文件如下:

[html] view
plain
 copy

print?

  1. <hibernate-mapping package="com.hibernate.domain" >
  2. <class name="Department" table="department">
  3. <id name="id" column="departmentID">
  4. <generator class="native" />
  5. </id>
  6. <property name="name" column="departmentName" />
  7. <set name="employees" inverse="true" table="department_employee">
  8. <!-- 指定关联的外键列 -->
  9. <key column="departmentID" />
  10. <!-- 用以映射到关联类属性 -->
  11. <many-to-many class="Employee" column="employeeID" unique="true"/>
  12. </set>
  13. </class>
  14. </hibernate-mapping>

Employee.hbm.xml配置文件保持不变

如果我们依然通过使用操作方法:保存一个Department、两个Employee,这个时候,系统应该是产生5条sql语句。其中两条是用于向连接表中插入记录,从而建立Department和Employee之间的关联关系。

2、双向1-N关联

对于1-N关联,Hibernate推荐使用双向关联,而且不要让1端控制关联关系,而使用N的一端控制关联关系。

双向的1-N关联,两端都需要增加对关联属性的访问,N的一端增加引用到关联实体的属性,1的一端增加集合属性,集合元素为关联实体。

两个持久化类和上面差不多,只需要在Employee中增加对Department的引用属性即可。

2.1无连接表的双向1-N关联

对于无连接表的双向1-N关联。N的一端需要增加<many-to-one.../>元素来映射关联属性,而1的一端需要使用<set.../>或者<bag.../>元素来映射关联属性。同时<set.../>或者<bag../>元素需要增加<key.../>子元素映射外键列,并且使用<one-to-many.../>子元素映射关联属性。

在前面已经提到对于1-N关联映射,通常不提倡1的一端控制关联关系,而应该由N的一端来控制关联关系。此时我们可以再<set.../>元素中指定inverse="true",用于指定1的一端不控制关联关系

Department映射文件:Department.hbm.xml

[html] view
plain
 copy

print?

  1. <hibernate-mapping package="com.hibernate.domain" >
  2. <class name="Department" table="department">
  3. <id name="id" column="departmentID">
  4. <generator class="native" />
  5. </id>
  6. <property name="name" column="departmentName" />
  7. <!-- 映射集合属合 -->
  8. <set name="employees" inverse="true" >
  9. <!-- 指定关联的外键列 -->
  10. <key column="departmentID" />
  11. <!-- 用以映射到关联类属性 -->
  12. <one-to-many class="Employee"/>
  13. </set>
  14. </class>
  15. </hibernate-mapping>

Employee映射文件:Employee.hbm.xml

[html] view
plain
 copy

print?

  1. <hibernate-mapping package="com.hibernate.domain">
  2. <class name="Employee" table="employee">
  3. <id name="id" column="employeeID">
  4. <generator class="native" />
  5. </id>
  6. <property name="name" column="employeeName" />
  7. <!-- 用于映射N-1关联实体,指定关联实体类为 :Department,指定外键为:departmentID-->
  8. <many-to-one name="department" column="departmentID" />
  9. </class>
  10. </hibernate-mapping>

下面的程序段用于保存一个Department对象和两个Employee对象

[java] view
plain
 copy

print?

  1. static void add(){
  2. Session session = HibernateUtil.getSession();
  3. Transaction tx = session.beginTransaction();
  4. Department department = new Department();
  5. department.setName("国防部");
  6. //建立两个对象
  7. Employee employee1 = new Employee();
  8. employee1.setName("chentmt1");
  9. employee1.setDepartment(department);      //建立两个对象的关联关系
  10. Employee employee2 = new Employee();
  11. employee2.setName("chentmt2");
  12. employee2.setDepartment(department);      //建立两个对象的关联关系
  13. session.save(department);          //....1
  14. session.save(employee2);
  15. session.save(employee1);
  16. tx.commit();
  17. session.close();
  18. }

SQL语句:

[sql] view
plain
 copy

print?

  1. Hibernate: insert into department (departmentName) values (?)
  2. Hibernate: insert into employee (employeeName, departmentID) values (?, ?)
  3. Hibernate: insert into employee (employeeName, departmentID) values (?, ?)

通过上面的SQL语句可以看出,Hibernate并不是采用哪种先insert后update的方式来插入employee记录的。而是通过一条insert SQL语句来执行的。为什么?因为程序持久化Employee实体之前,Employee已经知道它所关联Department实体(employee2.setDepartment(department);)。 所以为了保证比较好的性能,需要注意以下两个问题:

1、应该先持久化主表对象:Department。因为我们希望程序在持久化从表:Employee对象时,Hibernate可以为他的外键属性值分配值。

2、先设置两个持久化类(Department和Employee)的关系,再保存持久化从表对象(Employee)。如果顺序反过来,程序持久化Employee对象时,该对象还没有关联实体,所以Hibernate不能为对应记录的外键列指定值,等到设置关联关系时,Hibernate只能再次使用update语句来修改了。

2.2有连接表的双向1-N关联

有连接表的双向1-N关联。1的一端使用集合元素映射,然后在集合元素中增加<many-to-many../>子元素,该子元素映射到关联类。为保证该实体是1的一端,需要增加unique="true"属性。N的一端则使用<join.../>元素来强制使用连接表。

在N的一端使用<join../>元素强制使用连接表,因此将使用<key.../>子元素来映射连接表中外键列,器且使用<many-to-one../>来映射连接表中关联实体的外键列;反过来,1的一端也将在<set.../>元素中使用<key.../>和<many-to-many.../>两个子元素,他们也映射到连接表的两列。为了保证得到正确的映射关系,应该满足如下关系:<join.../>里<key.../>子元素的column属性和<set.../>里<man-to-many.../>元素的column属性相同;<join.../>里<many-to-many.../>子元素的column属性和<set../>元素的<key../>子元素的column属性应该相等。

下面就是Department和Employee两个持久化类的映射文件:

Department.hbm.xml

[html] view
plain
 copy

print?

  1. <hibernate-mapping package="com.hibernate.domain" >
  2. <class name="Department" table="department">
  3. <id name="id" column="departmentID">
  4. <generator class="native" />
  5. </id>
  6. <property name="name" column="departmentName" />
  7. <set name="employees" inverse="true" table="department_employee">
  8. <!-- 指定关联的外键列 -->
  9. <key column="departmentID" />
  10. <!-- 用以映射到关联类属性 -->
  11. <many-to-many class="Employee" column="employeeID" unique="true"/>
  12. </set>
  13. </class>
  14. </hibernate-mapping>

Employee.hbm.xml

[html] view
plain
 copy

print?

  1. <hibernate-mapping package="com.hibernate.domain">
  2. <class name="Employee" table="employee">
  3. <id name="id" column="employeeID">
  4. <generator class="native" />
  5. </id>
  6. <property name="name" column="employeeName" />
  7. <!-- 使用join元素强制使用连接表 -->
  8. <join table="department_employee">
  9. <!-- key映射外键列 -->
  10. <key column="employeeID" />
  11. <!-- 映射关联实体 -->
  12. <many-to-one name="department" column="departmentID" />
  13. </join>
  14. </class>
  15. </hibernate-mapping>
时间: 2024-11-09 06:09:38

Hibernate的关联映射之1-N关联映射的相关文章

Hibernate的七种映射关系之七种关联映射(二)

继续上篇博客 七.Hibernate双向一对多关联映射:让多的一端来维护关系. 主要是解决一对多单向关联的缺陷,而不是需求驱动的. 1.在Student.java实体类里添加Classes引用.private Classes classes; 2.Student.hbm.xml里添加many-to-one标签:<many-to-one name="classes" column="classesid"/>.Classes.hbm.xml在例子(六)里的那

Hibernate的七种映射关系之七种关联映射(一)

关联映射就是将关联关系映射到数据库里,在对象模型中就是一个或多个引用. 一.Hibernate多对一关联映射:就是在"多"的一端加外键,指向"一"的一端. 比如多个学生对应一个班级,多个用户对应一个级别等等,都是多对一关系. 1."多"端实体加入引用"一"端实体的变量及getter,setter方法. 比如说多个学生对应一个班级,在学生实体类加入:private Grade grade; 2."多"端配置文

一口一口吃掉Hibernate(五)——一对多单向关联映射

版权声明:本文为博主原创文章,未经博主允许不得转载.如需转载请声明:[转自 http://blog.csdn.net/xiaoxian8023 ] 在上一篇博客<一口一口吃掉Hibernate(四)--多对一单向关联映射>中,介绍了多对一的关联映射,今天就反过来说一下一对多的单向关联映射. 可能有人会对这2篇博客的题目有点混淆不清,跟日常说的关系有点不同.我们日常说的比如父子关系,夫妻关系都是说的双向关系,而现在讨论的则是单向关系,所以也就有了多对一和一对多的说法. 二者的关系其实很简单,只是

HIbernate学习笔记4 之 延迟加载和 关联映射

一.延迟加载 * 在使用hibernate一些方法的查询数据的时候,返回的只是一个空对象(除id外属性都为NULL),并没有真正的查询数据库,而是在使用这个对象时才会触发查询数据库,并将这些数据注入到这个空对象中.这种将查询实际推迟到对象访问的机制就称为 延迟加载. *好处: 可以提升内存资源的使用率,降低对数据库的访问次数. *方法: session.load()   /  query.iterate()  / 关联映射中对关联属性的加载 *注意问题:避免在使用对象之前,提前关闭session

hibernate之关于一对一单向,双向关联映射

[hibernate]之关于一对一单向,双向关联映射 首先我们来看,Hibernate官方对于一对一单向关联的解释: 基于外键关联的单向一对一关联和单向多对一关联几乎是一样的.唯一的不同就是单向一对一关 联中的外键字段具有唯一性约束. ok,那我们也可以这样理解,一对一其实就是多对一关联的特殊形式,我们知道Hibernate的配置方式有两种,分别是Annotations,XML两种配置方式! Annotations的一对一单向关联映射 人(User)和***号(Card) @Entity @Ta

Hibernate ORM框架——续第二章:Hibernate映射关系:单向关联

一:课堂笔记 **********单表映射*************** 1.只有一个实体类映射一个表 2.有一个实体类+ 一个或者多个值类型,合在一起 映射为一个表 **********多表映射*************** 定义:类有多个映射为多个表 **********数据库层面,如ORM无关************** 2个表之间要建立关系?该如何做?有多少种方法 假定有2个表,分别为A,B 3种关系 a)主键关联:A表的主键对应B表的主键,(一对一的关系) 在数据库种如何建立这种关系?

【SSH进阶之路】Hibernate映射——多对一单向关联映射(四)

[SSH进阶之路]Hibernate基本原理(一) ,小编介绍了Hibernate的基本原理以及它的核心,采用对象化的思维操作关系型数据库. [SSH进阶之路]Hibernate搭建开发环境+简单实例(二),小编搭建了基本Hibernate的开发环境,并做了一个简单实例,对它的基本原理有了一个理性的认识. [SSH进阶之路]Hibernate基本映射(三),我们介绍了Hibernate的基本映射(即对一个实体进行映射)的相关概念,并给大家实现相关实例,比较简单. 这篇博客,我们开始介绍基于基本映

Hibernate中用注解配置一对多双向关联和多对一单向关联

Hibernate中用注解配置一对多双向关联和多对一单向关联 Hibernate提供了Hibernate Annotations扩展包,使用注解完成映射.在Hibernate3.3之前,需单独下载注解开发包 配置持久化类 配置关联关系 下面我们先从多对一单向关联关系讲起,多对一单向关联就是在多的一方植入一的一方的主键作为外键,下面我们先进行初始配置, 在配置的过程中我们会遇到一个问题  就是无论用load还是get都不会出现延迟加载,那么我们应该如何设置为要延迟加载,这样做的好处是可以在用的时候

Hibernate映射(一)——基本映射

对象关系映射(ORM)的基本认识在之前的两篇博客中已有体现了. ORM基础认识 浅谈ORM的映射方案 今天来学习的是Hibernate的对象关系映射. Hibernate的几种映射方式 基本映射 Hibernate中映射的配置在XML中,我们要对object和table进行映射转换,只需要配置XML即可,所以学会使用Hibernate,重点也就在配置文件中的配置. 首先我们从基本映射开始,如下例: 1.User实体类: import java.util.Date; public class Us

Hibernate映射(一)—基本映射

对象关系映射(ORM)已经在前面的博文中介绍了:ORM介绍 本篇博文主要来介绍Hibernate的基本映射 概念 关系:事物之间相互作用.相互联系的状态 关联:将对象(数据库表)之间通过某种方式联系起来 映射:将一种形式转化为另一种形式 基本映射:表与表之间没有任何联系(系统中的字典表) Hibernate提供了几种映射方式 Hibernate基本组件 映射类(User.java):它是描述数据库表的结构,表中的字段被描述成属性,将来就可以实现把表中的记录映射成为该类的对象 映射文件(User.