利用关联关系操纵对象
数据对象之间关联关系有一对一、一对多及多对多关联关系。在数据库操作中,数据对象之间的关联关系使用JDBC处理很困难。本节讲解如何在 Hibernate中处理这些对象之间的关联关系。本节使用到4个类,它们分别是Student(学生)、Card(学生证)、Group(班级)和 Course(课程),它们之间的关联关系如图9-1所示。这些实体存在级联(cascade)问题。例如,当删除一个班级的信息时,还要删除该班的所有 学生的基本信息。如果直接使用JDBC执行这种级联操作,会非常烦琐。Hibernate通过把实体对象之间关联关系及级联关系在映射文件中声明,比较简 便地解决了这类级联操作问题。
图9-1 对象关联图
9.2.1 一对一关联关系的使用
一对一关系在实际生活中是比较常见的,例如学生与学生证的关系,通过学生证可以找到学生。一对一关系在Hibernate中的实现有两种方式,分别是主键关联和外键关联。
1.以主键关联
主键关联的重点是,关联的两个实体共享一个主键值。例如,Student与Card是一对一关系,它们在数据库中对应的表分别是t_student 和t_card。它们共用一个主键值id,这个主键可由t_student表或t_card表生成。问题是如何让另一张表引用已经生成的主键值呢?例 如,t-student表填入了主键id的值,t_card表如何引用它?这需要在Hibernate的映射文件中使用主键的foreign生成机制。
为了表示Student与Card之间的一对一关联关系,在Student和Card的映射文件Student.hbm.xml和Card.hbm.xml中都要使用<one-to-one>标记,如例程9-2所示。
例程9-2 Student.hbm.xml
-----------------------------------------------------------------------------------------------
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" >
<hibernate-mapping>
<class name="test.Student" table="T_STUDENT" lazy="true"><!-- 把类与表关联起来-->
<id name="id" column="id" type="int">
<generator class="increment" />
</id>
<property name="name" column="NAME" type="string" />
<!--property name="card_id" column="CARD_ID" type="int" /--> <!--映射学生证号-->
<property name="sex" column="SEX" type="string" />
<property name="age" column="AGE" type="int" />
<one-to-one name="card" class="test.Card"
fetch="join" cascade="all" />
</class>
</hibernate-mapping>
<class>元素的lazy属性设定为true,表示延迟加载,如果lazy的值设置为false,则表示立即加载。下面对立即加载和延迟加载这两个概念进行说明。
l 立即加载:表示Hibernate在从数据库中取得数据,组装好一个对象(比如学生1)后,会立即再从数据库取得数据,组装此对象所关联的对象(例如学生证1)。
l 延迟加载:表示Hibernate在从数据库中取得数据,组装好一个对象(比如学生1)后,不会立即再从数据库取得数据,组装此对象所关联的对象(例如学生证1),而是等到需要时,才会从数据库取得数据,组装此关联对象。
<one-to-one>元素的cascade属性表明操作是否从父对象级联到被关联的对象,它的取值如下。
l none:在保存、删除或修改对象时,不对其附属对象(关联对象)进行级联操作。这是默认设置。
l save-update:在保存、更新当前对象时,级联保存、更新附属对象(临时对象、游离对象)。
l delete:在删除当前对象时,级联删除附属对象。
l all:在所有情况下均进行级联操作,即包含save-update和delete操作。
l delete-orphan:删除和当前对象解除关系的附属对象。
<one-to-one>元素的fetch属性的可选值是join和select,默认值是select。当fetch属性设定为join时,表示连接抓取(Join fetching) : Hibernate通过 在SELECT语句使用OUTER JOIN(外连接)来获得对象的关联实例或者关联集合。 当fetch属性设定为select时,表示查询抓取(Select fetching):需要另外发送一条 SELECT 语句抓取当前对象的关联实体或集合。
例程9-3中<one-to-one>元素的cascade属性设置为“all”,表示增加、删除及修改Student对象时,都会级联增加、删除和修改Card对象。
例程9-3 Card.hbm.xml
-----------------------------------------------------------------------------------------------
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" >
<hibernate-mapping>
<class name="test.Card" table="t_card" lazy="true"><!-- 把类与表关联起来-->
<id name="id" column="id">
<generator class="foreign" >
<param name="property">student</param>
</generator>
</id>
<one-to-one name="student" class="test.Student" constrained="true"/>
<property name="name" column="name" type="string" />
<!-- one-to-one name="student" class="test.Student" constrained="true"/-->
</class>
</hibernate-mapping>
在例程9-3中,Card.hbm.xml的主键id使用外键(foreign)生成机制,引用代号为“student”对象的主键作为Card表 的主键和外键。student在该映射文件的<one-to-one>元素中进行了定义,它是Student对象的代号。<one- to-one>元素的属性Constrained="true"表示Card引用了student的主键作为外键。
需要特别注意的是,Student类中要相应地加入一对get/set方法:
public Card getCard() {
return this.card;
}
public void setCard(Card card) {
this.card = card;
}
在Card类中也要相应地加入一对get/set方法:
public Student getStudent() {
return this.stu;
}
public void setStudent(Student stu) {
this.stu = stu;
}
在客户端测试程序中操纵Student和Card对象的方法如例程9-4所示。
例程9-4 客户端测试程序
package test;
import org.hibernate.*;
import org.hibernate.cfg.*;
import java.io.File;
import java.util.List;
public class Test {
public static void main(String[] args) {
File file = new File("D:\\eclipse3.2\\workspace\\HibernateTest\\hibernate.cfg.xml");
Configuration conf = new Configuration().configure(file);
SessionFactory sf = conf.buildSessionFactory();
Session session = sf.openSession();
Transaction tx = session.beginTransaction();
//新建Student对象
Student stu = new Student();
stu.setName("Walker");
stu.setSex("male");
stu.setAge(22);
//新建Card对象
Card card = new Card();
card.setName("Walker");
//设置Student对象与Card对象之间的关联
stu.setCard(card);
card.setStudent(stu); //此句不能省略,否则card将不知从何处取得主键值
try {
session.save(stu);
tx.commit();
session.close();
System.out.println("Data have been inserted into DB.");
} catch (HibernateException e) {
e.printStackTrace();
tx.rollback();
session.close();
}
}
}
运行以上代码后,将会在t_student表和t_card表中插入相应的数据。
2.以外键关联
以外键关联的要点是:两个实体各自有不同的主键,但其中一个实体有一个外键引用另一个实体的主键。例如,假如Student和Card是外键关联的 一对一关系,它们在数据库中相应的表分别是t_student表和t_card表,t_student表有一个主键id,t_card表有一个主键id和 一个外键stu_id,此外键对应student表的主键id。
Student的映射文件Student.hmb.xml见例程9-2。但Card的映射文件Card.hbm.xml要做相应变动,如例程9-5所示。
例程9-5 Card.hbm.xml
----------------------------------------------------------------------------------------------------------------------
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd">
<hibernate-mapping>
<class name="test.Card" table="T_CARD" lazy= "true"><!--把类与表关联起来-->
<id name="id" >
<generator class="increment" ><!--不再是foreign了-->
</generator>
</id>
<property name="name" column="NAME" type="string" />
<many-to-one name="student" class="Student" column="stu_id"
unique="true"/> <!--唯一的多对一,实际上变成一对一关系了-->
</class>
</hibernate-mapping>
在例程9-5中,<many-to-one>元素的name属性声明外键关联对象的代号,class属性声明该外键关联对象的类,column属性声明该外键在数据表中对应的字段名,unique属性表示使用DDL为外键字段生成一个唯一约束。
以外键关联对象的一对一关系,其实本质上变成了一对多的双向关联了,应直接按照一对多和多对一的要求编写它们的映射文件。当<many-to-one>元素的unique属性设定为true,多对一的关系实际上变成了一对一的关系。
在客户端程序中操纵外键关联一对一关系的对象的方法见例程9-4。
9.2.2 一对多关联关系的使用
一对多关系很常见,例如父亲和孩子、班级与学生的关系就是很好的一对多的关系。在实际编写程序时,一对多关系有两种实现方式:单向关联和双向关联。 单向的一对多关系只需在一方进行映射配置,而双向的一对多需要在关联的双方进行映射配置。下面以Group(班级)和Student(学生)为例讲解如何 配置一对多的关系。
1.单向关联
单向的一对多关系只需在一方进行映射配置,所以我们只配置Group(班级)的映射文件Group.hbm.xml,如例程9-6所示。
例程9-6 Group.hbm.xml
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd">
<hibernate-mapping>
<class name="test.Group" table="T_GROUP" lazy="true"><!--把类与表关联起来-->
<id name="id" column="ID"type="int">
<generator class="increment" >
</generator>
</id>
<property name="name" column="NAME" type="string"
update="true" insert="true" />
<set name="students"
table="T_STUDENT"
lazy="false"
inverse="false"
cascade="all"
sort="unsorted"
>
<key column="ID"/>
<one-to-many class="test.Student"/>
</set>
</class>
</hibernate-mapping>
在以上映射文件中,<property>元素的insert属性表示被映射的字段是否出现在SQL的 INSERT语句中;update属性表示被映射的字段是否出现在SQL的 UPDATE语句中。
<set>元素描述的字段(本例中为students)对应的类型为java.util.Set,它的各个属性的含义如下。
l name:字段名,本例的字段名为students,它属于java.util.Set类型。
l table:关联表名,本例中,students的关联数据表名是t_student。
l lazy:是否延迟加载,lazy=false表示立即加载。
l inverse:用于表示双向关联中的被动方的一端,inverse的值为false的一方负责维护关联关系。默认值为false。本例中Group将负责维护它与Student之间的关联关系。
l cascade:级联关系;cascade=all表示所有情况下均进行级联操作,即包含save-update和delete操作。
l sort:排序关系,其可选取值为unsorted(不排序)、natural(自然排序)、comparatorClass(由某个实现了java.util.comparator接口的类型指定排序算法)。
<key>子元素的column属性指定关联表(本例中t_student表)的外键,<one-to-many>子元素的class属性指定了关联类的名字。
此外,在Group类中增加如下get/set方法:
private Set students;
public Set getStudents() {
return this.students;
}
public void setStudents(Set stu) {
this.students = stu;
}
假如我们想为一个班级添加一个学生对象,实现的代码如下:
Transaction tx = session.beginTransaction();
Student stu = new Student();
stu.setName("Walker");
stu.setSex("male");
stu.setAge(22);
group.getStudents().add(stu);
session.save(group);
tx.commit();
2.双向关联
如果要设置一对多双向关联,那么还需要在“多”方的映射文件中使用<many-to-one>标记。例如,在Group与 Student一对多的双向关联中,除了Group的映射文件Group.hbm.xml和Group类进行设置和修改外,还需要在Student的映射 文件Student.hbm.xm中加入:
<many-to-one
name="group"
class="test.Group"
cascade="none"
outer-join="auto"
update="true"
insert="true"
column="ID"
/>
name、class等属性前面已经解释过了,这里只说明insert和update属性。insert和update设定是否对column属性指定的关联字段进行insert和update操作。在Student类还要相应添加一对get/set方法:
public Group getGroup() {
return this.group;
}
public void setGroup(Group g) {
this.group = g;
}
此外,把Group.hbm.xml(如例程9-6所示)中的<set>元素的inverse属性的值设定为true,如下所示。
<set name="students" table="T_STUDENT" lazy="false"
inverse="true" cascade="all" sort="unsorted">
<key column="ID"/>
<one-to-many class="Student"/>
</set>
当Group.hmb.xml中<set>元素的inverse属性的值设定为false时,Group和Student之间的关联关 系由Group维护,Group负责将自己的id告诉Student,然后Hibernate发送update语句去更新记录。但现在inverse的值 设定为true后,Group和Student之间的关联关系转由Student来维护,由Student自动去取得Group的id,而这个 Student取得Group的id的动作,其实就是完成一个“学生添加到班级”的动作。
9.2.3 多对多关联关系的使用
Student(学生)和Course(课程)的关系就是多对多的关系。在映射多对多关系时,需要另外使用一个连接表(例 如,Student_Course)。Student_Course表包含2个字段:CourseId和StuId。此外,在它们的映射文件中使 用<many-to-many>标记。
Student的映射文件Student.hbm.xml中加入以下描述信息:
<set name="courses" table=" Student_Course" lazy="false"
inverse="false" cascade="save-update" >
<key column="StuId"/>
<many-to-many class="test.Course" column="CourseId" />
</set>
相应地,Course的映射文件Course.hbm.xml加入以下描述信息:
<set name="students" table=" Student_Course" lazy="false"
inverse="true" cascade="save-update" >
<key column="CourseId"/>
<many-to-many class="test.Student" column="StuId" />
</set>
1.添加关联关系
首先让我们编一个程序来看看一个名为Bill的学生选择了什么课程:
……
//获得包含Bill的Student对象
Student stu = (Student) session.createQuery(“from Student s where s.name =
‘Bill’ ”) .uniqueResult();
List ls = new ArrayList(stu.getCourses());
for(int i=0; i<ls.size(); i++) {
Course course = (Course)ls.get(i); //获得Course对象
System.out.println(course.getName()); //打印Bill所选课程的清单
}
…..
现在Bill还想选修business课程,这对于程序员来说只是为Bill添加了一个到business的关联,也就是说在student_course表中新添一条记录,而T_Student 和T_Course表都不用变更。
……
Student stu = (Student) session.createQuery(“from Student s where s.name = ‘Bill’ ”) .uniqueResult();
Course course = (Course) session.createQuery(“from Course c where c.name =
‘business’ ”) .uniqueResult();
//设置stu与course的相互关系
stu.getCourses().add(course);
course.getStudents().add(stu);
…..
2.删除关联关系
删除关联关系比较简单,直接调用对象集合的remove()方法删除不要的对象即可。例如,要从学生Bill的选修课清单中删除politics和chemistry两门课,程序如下:
…….
Student stu = (Student) session.createQuery("from Student s where s.name = 'Bill' ") .uniqueResult();
Course course1 = (Course) session.createQuery("from Course c where c.name =
'politics' ") .uniqueResult();
Course course2 = (Course) session.createQuery("from Course c where c.name =
'chemistry' ") .uniqueResult();
stu.getCourse().remove(course1); //删除politics课程
stu.getCourse().remove(course2); //删除chemisty课程
…….
运行以上语句将从student_course表中删除这两条记录,但T_Student和T_Course表没有任何变化。
相关推荐
本文将深入探讨“hibernate关联映射实例”中的关键知识点,包括一对多、多对多和继承映射,这些都是Hibernate中至关重要的概念。 1. **一对多关联映射**: 在现实世界中,一个实体可能会与多个其他实体相关联,...
总之,Hibernate关联关系是理解其工作原理的关键,它使得Java对象和数据库记录之间的映射变得简单。通过练习,你可以掌握如何配置和使用这些关系,以及如何进行相关的查询操作,从而提升你的开发效率和代码质量。...
一、Hibernate关联关系介绍 在Hibernate中,关联关系主要包括以下几种: 1. 一对一(OneToOne):一个实体对应另一个实体的唯一实例。 2. 一对多(OneToMany):一个实体可以与多个其他实体相关联。 3. 多对一...
本文将以“Hibernate关联关系”为主题,详细介绍Hibernate中关联关系的创建及使用方法。 #### 关联关系概述 在关系型数据库中,实体之间通常存在一对一、一对多、多对一和多对多等关联关系。对于这些关系,...
Hibernate关联关系是Java持久化框架Hibernate中的核心概念,它允许我们在数据库中建立对象之间的关系映射,以便在程序中操作对象时,可以自动处理与数据库的交互。本篇将深入探讨Hibernate的四种主要关联关系:一对...
在 Hibernate 映射中,需要使用 `one-to-one` 元素,并设置 `constrained="true"`,表明该类的主键由关联类生成。 3. Hibernate 一对一连接表单向关联: 这种关联方式是通过额外的连接表来实现一对一关系的。连接...
在Java的持久化框架Hibernate中,一对一(OneToOne)关系是一种常见的对象关系映射(ORM)配置,用于表示两个实体类之间的一种唯一关联。在这个场景中,“hibernate使用主键关联的一对一关系”指的是通过共享主键来...
这篇博客"Hibernate关联关系疑问"可能探讨了在使用Hibernate处理对象关系映射(ORM)时遇到的一些关联问题。在ORM中,关联关系是数据库表之间的连接,如一对一、一对多、多对一和多对多关系。 1. **一对一关联...
在深入探讨Hibernate关联关系映射分类之前,我们首先简要回顾一下Hibernate框架的基本概念。Hibernate是一种持久层框架,主要用于Java应用程序中的对象关系映射(ORM),它能够将面向对象的数据模型转换为数据库中的...
在本项目中,"hibernate关联关系2"是一个关于使用Hibernate框架处理数据库关联关系的实战案例。Hibernate是一个流行的开源对象关系映射(ORM)工具,它允许开发人员使用Java对象来操作数据库,极大地简化了数据库...
在 Hibernate 中,对象的状态管理是至关重要的,因为它直接影响到对象如何与数据库交互。以下是 Hibernate 中对象的三种状态以及相关的保存方法: 1. **瞬时对象(Transient Objects)** - 当使用 `new` 关键字...
在Java持久化框架Hibernate中,对象关系模型(Object-Relational Mapping,ORM)是将数据库中的数据映射到Java对象的技术。它使得开发者能够用面向对象的方式来操作数据库,简化了数据库编程的过程。以下是对...
- Hibernate提供两种加载关联对象的策略:懒加载(Lazy)和急加载(Eager)。默认情况下,一对一关联是懒加载的,意味着关联的对象不会在初始加载时被加载,而是等到真正需要时才从数据库中获取。如果希望关联对象...
在这个“hibernate关联关系实例”中,我们将深入探讨四种基本的关联关系:一对一(One-to-One)、一对多(One-to-Many)、多对一(Many-to-One)以及多对多(Many-to-Many),同时也会涉及Hibernate查询语言(HQL)...
"hibernate关联关系映射"是Hibernate的核心概念之一,它定义了如何在Java对象和数据库表之间建立关系。以下是对这个主题的详细讲解: 一、一对一关系映射(One-to-One) 一对一关系是指一个实体对应另一个实体的...
本教程“Hibernate关联映射.rar”聚焦于Hibernate中的关联映射,特别是多对一和一对多的关系映射,非常适合初学者了解和实践。 首先,我们要理解什么是关联映射。在数据库中,表与表之间存在各种关系,如一对一、一...