首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 开发语言 > 编程 >

hibernate联系关系对象的使用

2013-11-23 
hibernate关联对象的使用利用关联关系操纵对象?数据对象之间关联关系有一对一、一对多及多对多关联关系。在

hibernate关联对象的使用
利用关联关系操纵对象

?

数据对象之间关联关系有一对一、一对多及多对多关联关系。在数据库操作中,数据对象之间的关联关系使用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 />

?

</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"? 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 >

?

??? <param name="property">student</param>

?

??? </generator>

?

</id>

?

<one-to-one name="student"? constrained="true"/>

?

<property name="name" column="name" type="string" />

?

<!-- one-to-one name="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 ><!--不再是foreign了-->

?

??? </generator>

?

</id>

?

<property name="name" column="NAME" type="string" />

?

<many-to-one? name="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 >

?

??? </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 table="T_STUDENT" lazy="false"

?

??? inverse="true" cascade="all" sort="unsorted">

?

<key column="ID"/>

?

<one-to-many ?table=" Student_Course" lazy="false"

?

? ??inverse="false" cascade="save-update" >

?

<key column="StuId"/>

?

<many-to-many 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 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表没有任何变化。?

?

热点排行