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-onename="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表没有任何变化。
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
第1章 Java应用分层架构及软件模型 1 本章介绍软件的分层结构、关系数据模型和域模型等概念。Hibernate位于持久化层,是域模型和关系数据模型之间的桥梁。 1.1 应用程序的分层体系结构 1 1.1.1 区分物理层和逻辑层 2 1.1.2 软件层的特征 3 1.1.3 软件分层的优点 4 1.1.4 软件分层的缺点 4 1.1.5 Java应用的持久化层 5 1.2 软件的模型 6 1.2.1 概念模型 7 1.2.2 关系数据模型 8 1.2.3 域模型 10 1.2.4 域对象 10 1.2.5 域对象之间的关系 11 1.2.6 域对象的持久化概念 17 1.3 小结 19 1.4 思考题 19 第2章 Java对象持久化技术概述 21 业务数据在内存中表现为实体对象形式,而在关系数据库中表现为关系数据形式。数据访问代码负责把实体对象持久化到关系数据库中。 2.1 直接通过JDBC API来持久化实体对象 21 .2.2 ORM简介 27 2.2.1 对象-关系映射的概念29 2.2.2 ORM中间件的基本使用方法 31 2.2.3 常用的ORM中间件 33 2.3 实体对象的其他持久化模式 34 2.3.1 主动域对象模式 35 2.3.2 JDO模式 37 2.3.3 CMP模式 37 2.4 Hibernate API简介 38 2.4.1 Hibernate的核心接口 39 2.4.2 事件处理接口 41 2.4.3 Hibernate映射类型接口 41 2.4.4 可供扩展的接口 42 2.5 小结 43 2.6 思考题 45 第3章 第一个Hibernate应用 47 本章通过简单的helloapp应用例子,演示如何利用Hibernate来持久化Java对象。 3.1 创建Hibernate的配置文件 47 3.2 创建持久化类 48 3.3 创建数据库Schema 51 3.4 创建对象-关系映射文件 52 3.4.1 映射文件的文档类型定义(DTD) 52 3.4.2 把Customer持久化类映射到CUSTOMERS表 54 3.5 通过Hibernate API操纵数据库 58 3.5.1 Hibernate的初始化 61 3.5.2 访问Hibernate的Session接口 63 3.6 运行helloapp应用 67 3.6.1 创建运行本书范例的系统环境 67 3.6.2 创建helloapp应用的目录结构 72 3.6.3 把helloapp应用作为独立应用程序运行 73 3.6.4 把helloapp应用作为Java Web应用运行 77 3.7 小结 78 3.8 思考题 80 第4章 hbm2java和hbm2ddl工具 83 本章介绍Hibernate提供的两个工具hbm2java和hbm2ddl,它们能简化软件开发过程。 4.1 创建对象-关系映射文件 83 4.1.1 定制持久化类 85 4.1.2 定制数据库表 88 4.2 建立项目的目录结构 90 4.3 运行hbm2java工具 93 4.4 运行hbm2ddl工具 94 4.5 使用XML格式的配置文件 96 4.6 小结 97 4.7 思考题 98 第5章 对象-关系映射基础 101 本章主要介绍单个持久化类与单个数据库表之间进行映射的方法,尤其是当持久化类的属性不和数据库表的字段一一对应时的映射技巧。 5.1 持久化类的属性及访问方法 101 5.1.1 基本类型属性和包装类型属性 102 5.1.2 Hibernate访问持久化类属性的策略 104 5.1.3 在持久化类的访问方法中加入程序逻辑 104 5.1.4 设置派生属性 107 5.1.5 控制insert和update语句 108 5.2 处理SQL引用标识符 109 5.3 创建命名策略 110 5.4 设置数据库Schema 112 5.5 设置类的包名 113 5.6 运行本章的范例程序 114 5.7 小结 120 5.8 思考题 121 第6章 映射对象标识符 123 本章主要介绍关系数据库中的代理主键(不具有业务含义),接着介绍Hibernate提供的几种内置标识符生成器的用法及适用范围。 6.1 关系数据库按主键区分不同的记录 123 6.1.1 把主键定义为自动增长标识符类型 123 6.1.2 从序列(Sequence)中获取自动增长的标识符 124 6.2 Java语言按内存地址区分不同的对象 125 6.3 Hibernate对象标识符(OID)来区分对象 126 6.4 Hibernate的内置标识符生成器的用法 128 6.4.1 increment标识符生成器 131 6.4.2 identity标识符生成器 133 6.4.3 sequence标识符生成器 134 6.4.4 hilo标识符生成器 135 6.4.5 native标识符生成器 137 6.5 映射自然主键 138 6.5.1 映射单个自然主键 138 6.5.2 映射复合自然主键 140 6.6 小结 143 6.7 思考题 144 第7章 映射一对多关联关系 147 本章介绍一对多关联关系的映射方法,重点介绍inverse属性和cascade属性的用法。本章还将介绍通过Hibernate API来保存、修改和删除具有关联关系对象的方法。 7.1 建立多对一的单向关联关系 148 7.1.1 [many-to-one]元素的not-null属性 153 7.1.2 级联保存和更新 155 7.2 映射一对多双向关联关系 156 7.2.1 [set]元素的inverse属性 161 7.2.2 级联删除 163 7.2.3 父子关系 164 7.3 映射一对多双向自身关联关系 165 7.4 改进持久化类 171 7.5 小结 175 7.6 思考题 176 第8章 通过Hibernate操纵对象(上) 179 本章站在持久化层的角度,Java对象在生命周期中可处于临时状态、持久化状态、删除状态和游离状态。处于持久化状态的Java对象位于一个Session实例的缓存中,Session能根据这个对象的属性变化来同步更新数据库。 8.1 Java对象在JVM中的 生命周期 179 8.2 理解Session的缓存 181 8.2.1 Session的缓存的作用 182 8.2.2 脏检查及清理缓存的机制 184 8.3 Java对象Hibernate 持久化层的状态 187 8.3.1 临时对象的特征 188 8.3.2 持久化对象的特征 189 8.3.3 被删除对象的特征 190 8.3.4 游离对象的特征 191 8.4 Session接口的详细用法 191 8.4.1 Session的save()和persist()方法 191 8.4.2 Session的load()和get()方法 194 8.4.3 Session的update()方法 195 8.4.4 Session的saveOrUpdate()方法 197 8.4.5 Session的merge()方法 198 8.4.6 Session的delete()方法 200 8.4.7 Session的replicate()方法 201 8.5 级联操纵对象图 202 8.5.1 级联保存临时对象 206 8.5.2 更新持久化对象 207 8.5.3 持久化临时对象 207 8.5.4 更新游离对象 209 8.5.5 遍历对象图 210 8.6 小结 211 8.7 思考题 211

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值