Hibernate利用关联关系操纵对象

利用关联关系操纵对象

数据对象之间关联关系有一对一、一对多及多对多关联关系。在数据库操作中,数据对象之间的关联关系使用JDBC处理很困难。本节讲解如何在Hibernate中处理这些对象之间的关联关系。本节使用到4个类,它们分别是Student(学生)、Card(学生证)、Group(班级)和Course(课程),它们之间的关联关系如图1-1所示。这些实体存在级联(cascade)问题。例如,当删除一个班级的信息时,还要删除该班的所有学生的基本信息。如果直接使用JDBC执行这种级联操作,会非常烦琐。Hibernate通过把实体对象之间关联关系及级联关系在映射文件中声明,比较简便地解决了这类级联操作问题。



图1-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>标记,如例程1-2所示。

例程1-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,则表示立即加载。下面对立即加载和延迟加载这两个概念进行说明。

立即加载:表示Hibernate在从数据库中取得数据,组装好一个对象(比如学生1)后,会立即再从数据库取得数据,组装此对象所关联的对象(例如学生证1)。

延迟加载:表示Hibernate在从数据库中取得数据,组装好一个对象(比如学生1)后,不会立即再从数据库取得数据,组装此对象所关联的对象(例如学生证1),而是等到需要时,才会从数据库取得数据,组装此关联对象。

<one-to-one>元素的cascade属性表明操作是否从父对象级联到被关联的对象,它的取值如下。none:在保存、删除或修改对象时,不对其附属对象(关联对象)进行级联操作。这是默认设置。save-update:在保存、更新当前对象时,级联保存、更新附属对象(临时对象、游离对象)。delete:在删除当前对象时,级联删除附属对象。all:在所有情况下均进行级联操作,即包含save-update和delete操作。delete-orphan:删除和当前对象解除关系的附属对象。

<one-to-one>元素的fetch属性的可选值是join和select,默认值是select。当fetch属性设定为join时,表示连接抓取(Join fetching) : Hibernate通过 在SELECT语句使用OUTER JOIN(外连接)来获得对象的关联实例或者关联集合。当fetch属性设定为select时,表示查询抓取(Select fetching):需要另外发送一条 SELECT 语句抓取当前对象的关联实体或集合。

例程1-3中<one-to-one>元素的cascade属性设置为“all”,表示增加、删除及修改Student对象时,都会级联增加、删除和修改Card对象。

例程1-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>

在例程1-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对象的方法如例程1-4所示。例程1-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见例程1-2。但Card的映射文件Card.hbm.xml要做相应变动,如例程1-5所示。例程1-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>

在例程1-5中,<many-to-one>元素的name属性声明外键关联对象的代号,class属性声明该外键关联对象的类,column属性声明该外键在数据表中对应的字段名,unique属性表示使用DDL为外键字段生成一个唯一约束。

以外键关联对象的一对一关系,其实本质上变成了一对多的双向关联了,应直接按照一对多和多对一的要求编写它们的映射文件。当<many-to-one>元素的unique属性设定为true,多对一的关系实际上变成了一对一的关系。

在客户端程序中操纵外键关联一对一关系的对象的方法见例程1-4。

一对多关联关系的使用

一对多关系很常见,例如父亲和孩子、班级与学生的关系就是很好的一对多的关系。在实际编写程序时,一对多关系有两种实现方式:单向关联和双向关联。单向的一对多关系只需在一方进行映射配置,而双向的一对多需要在关联的双方进行映射配置。下面以Group(班级)和Student(学生)为例讲解如何配置一对多的关系。

1.单向关联

单向的一对多关系只需在一方进行映射配置,所以我们只配置Group(班级)的映射文件Group.hbm.xml,如例程1-6所示。

例程1-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,它的各个属性的含义如下。

name:字段名,本例的字段名为students,它属于java.util.Set类型。table:关联表名,本例中,students的关联数据表名是t_student。lazy:是否延迟加载,lazy=false表示立即加载。inverse:用于表示双向关联中的被动方的一端,inverse的值为false的一方负责维护关联关系。默认值为false。本例中Group将负责维护它与Student之间的关联关系。cascade:级联关系;cascade=all表示所有情况下均进行级联操作,即包含save-update和delete操作。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(如例程1-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的动作,其实就是完成一个“学生添加到班级”的动作。

多对多关联关系的使用

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

时间: 2024-10-12 21:01:06

Hibernate利用关联关系操纵对象的相关文章

攻城狮在路上(壹) Hibernate(七)--- 通过Hibernate操纵对象(下)

一.与触发器协同工作: 当Hibernate与数据库的触发器协同工作时,会出现以下两类问题: 1.触发器使Session缓存中的数据和数据库中的不一致: 出现此问题的原因是触发器运行在数据库内,它执行的操作对Session是透明的. 解决方案:在执行完包含有触发器的操作之后,立刻调用Session的flush()和refresh()方法,迫使Session的缓存与数据库同步. 2.Session的update()方法盲目的激发触发器: 这种情况主要发生在Session调用update()或sav

精通Hibernate——级联操纵对象

当我们加载一个类时session会加载所有和该类直接关联或者间接关联的对象. 在对象关系映射文件中,用于映射持久化类之间关联关系的元素,如.和元素,都有一个cascade属性,他用于指定如何操纵与当前关联的对象,他的可选属性如下: none:在保存.更新或删除对象时,忽略其他关联的对象,他是cascade默认属性 save-update:当通过session的save.update以及saveOrUpdate方法来保存或更新当前对象时,级联保存所有关联的新建的临时对象,并且级联更新所有关联的游离

Hibernate之配置文件以及对象关系映射文件了解

声明:CSDN不知道怎么了,有时候就是发表不了博文.本来就是一天一篇博文,最近搞的我很郁闷.有时候一天,有时候两三天都不能发.这就属于不可抗力.没办法的,啥时能发,我就发了. 一.学习案例:关于hibernate的配置文件以及对象关系映射文件的了解. 二.案例分析: a)hibernate配置文件(hibernate.cfg.xml) 1.hibernate.dialect:数据库使用的SQL方言 2.hiebrnate.connection.driver_class:数据库的驱动程序 3.hi

Hibernate复习(二)主要对象

1.SessionFactory 一个SessionFactory实例对应一个数据存储源,应用从SessionFactory中获得Session实例. SessionFactory有以下特点: –它是线程安全的,这意味着它的同一个实例可以被应用的多个线程共享. –它是重量级的,这意味着不能随意创建或销毁它的实例.如果应用只访问一个数据库,只需要创建一个SessionFactory实例,在应用初始化的时候创建该实 例.如果应用同时访问多个数据库,则需要为每个数据库创建一个单独的SessionFac

Hibernate三 关联关系之单向关联

一 分类Hibernate的关联关系可分为:单向关联和双向关联单向关联包括:1->1 1->N N->1 N->N 双向关联包括:1->1 1->N N->N二 单向N->1关联1.程序应该在N的一端的持久化类中增加一个属性,该属性引用1的一端的关联实体对于N->1关联,都需要在N的一端使用@ManyToOne修饰代表关联实体的属性,该注解可以指定以下属性:(1)cascade:指定Hibernate对关联实体采用怎样的级联策略,包括以下五种情况:Ca

Hibernate之关联关系映射(一对一主键映射和一对一外键映射)

1:Hibernate的关联关系映射的一对一外键映射: 1.1:第一首先引包,省略 1.2:第二创建实体类: 这里使用用户信息和身份证信息的关系,用户的主键编号既可以做身份证信息的主键又可以做身份证信息的外键,这里先做外键. 创建User.java: 用户和身份证一对一的关联关系映射       private IdCart idCart; IdCart.java: 身份证和用户,一对一的关系       private User user; 1 package com.bie.bean; 2

【Hibernate步步为营】--核心对象+持久对象全析(二)

上篇文章讨论了Hibernate的核心对象,在开发过程中经常用到的有JTA.SessionFactory.Session.JDBC,其中SessionFactory可以看做数据库的镜像,使用它能够创建Session对象,JTA用来管理事务,在对象模型修改后同步到数据库中,另外还有Hibernate作为持久层它封装了持久层的转化过程,下面着重讨论持久对象的转换过程. 一.状态解析 Hibernate的持久对象主要分为三个状态,Transient.Persistent.Detached,其中Tran

Rhythmk 学习 Hibernate 08 - Hibernate annotation 关联关系注解

1.一对一 (One to One)    共三种情况:     1.1 主键共享    1.2 外键共享 1.3 中间表关联 1.1  code: ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @Entity public class article {     @Id     @GeneratedValue    public Integer getArticleId() {         return articleId;     }     .....  

C#利用反射来判断对象是否包含某个属性的实现方法

本文实例展示了C#利用反射来判断对象是否包含某个属性的实现方法,对于C#程序设计人员来说有一定的学习借鉴价值. 具体实现代码如下: 1 /// <summary> 2 /// 利用反射来判断对象是否包含某个属性 3 /// </summary> 4 /// <param name="instance">object</param> 5 /// <param name="propertyName">需要判断的