分享
 
 
 

在Hibernate中实现复杂的数据映射

王朝other·作者佚名  2008-05-31
窄屏简体版  字體: |||超大  

内容:

案例介绍

创建Hibernate持久对象

描述对象之间的关系

使用映射关系

测试

行动起来

参考资料

关于作者

相关内容:

TCP/IP 介绍

TCP/IP 介绍 --

Java 专区中还有:

教学

工具与产品

代码与组件

所有文章

实用技巧

陈亚强 (cyqcims@mail.tsinghua.edu.cn)

高级软件工程师

2003 年 10 月

在前一篇文章《使用Hibernate来操作持久对象》中,介绍了Hibernate的基本概念,然后用实例演示了怎么在Web应用中使用Hibernate来封装持久数据对象。然而在现实的项目中,我们往往需要操作多个数据表,并且多个表之间往往存在复杂的关系,在本文,将介绍怎么在Hibernate中描述多个表的映射关系,并且演示怎么操作关系复杂的持久对象。

阅读本文前您需要以下的知识和工具:

Tomcat 5.09,可以从www.apache.org下载;

Hibernate2.0 相关运行环境,可以从http://hibernate.bluemars.net/ 下载;

至少安装了一个数据库服务器并且有相关的JDBC驱动程序。

本文的参考资料见参考资料

本文的全部代码在这里下载

案例介绍

在第一篇文章中,我们对一个表进行了简单的封装。在这篇文章中,我们讨论更加复杂的情况。

在这个例子中,将考虑到表之间的一对一、一对多、多对多的情况。如图1所示。

图1 实体之间的映射关系

在上面的数据模型图中,Student是所有表的核心,它和Classes表是一对多的关系,和Course表是多对多的关系(通过Student_Course_Link表来链接),和Address表是一对一的关系。

通过分析,我们可以把上面的数据模型转换成如下的Java持久对象,如图2所示。

图2 持久对象之间的关系

可以看出,数据模型图和Java持久对象的类图有非常大的相似性,但是不完全相同。比如Classes表和Student表是一对多的关系;在类图中,两者仍然是一对多的关系,但是在Classes类中添加了一个student属性,属性的类型是java.util.Set,它表示Classes对象中包含的所有Student对象。

创建Hibernate持久对象

已经对数据模型经过了分析,现在就可以创建持久对象了。持久对象之间的关系由图2所示的类图指定。

我们首先来看Student类,它是这个关系映射的核心,代码如例程1所示。

例程1 Student持久对象(Student.java)

package com.hellking.study.hibernate;

import java.util.Set;

/**

*在hibernate中代表了Students表的类。

*/

public class Student

{

/**属性,和students表中的字段对应**/

private String id;

private String name;

/**和其它类之间的映射关系**/

private Set courses;

private Classes classes;

private Address address;

/**属性的访问方法,必须是公共的方法**/

public void setId(String string) {

id = string;

}

public String getId() {

return id;

}

public void setName(String name)

{

this.name=name;

}

public String getName()

{

return this.name;

}

/**操作和其它对象之间的关系**/

public void setCourses(Set co)

{

this.courses=co;

}

public Set getCourses()

{

return this.courses;

}

public void setAddress(Address ad)

{

this.address=address;

}

public Address getAddress()

{

return this.address;

}

public void setClasses(Classes c)

{

this.classes=c;

}

public Classes getClasses()

{

return this.classes;

}

}

在Student类中,由于Students表和Classes的表是多对一的关系,故它包含了一个类型为Classes的classes属性,它的实际意义是一个学生可以有一个班级;Students表和Address的表是一对一的关系,同样也包含了一个类型为Address的address属性,它的实际意义是一个学生有一个地址;Students表和Course是多对多的关系,故它包含了一个类型为java.util.Set的course属性,它的实际意义是一个学生可以学习多门课程,同样,某个课程可以由多个学生来选修。

Classes对象和Student对象是一对多的关系。Classes对象包含一个类型为java.util.Set的students属性,它的代码如例程2所示。

例程2 Classes持久对象(Classes.java)

package com.hellking.study.hibernate;

import java.util.Set;

/**

*在hibernate中代表了Classes表的类。

*/

public class Classes

{

/**属性,和classes表的字段一致**/

private String id;

private String name;

/**和其它类之间的映射关系**/

private Set students;

/**属性的访问方法,必须是公共的方法**/

public void setId(String string) {

id = string;

}

public String getId() {

return id;

}

public void setName(String name)

{

this.name=name;

}

public String getName()

{

return this.name;

}

/**操作和其它对象之间的关系**/

public void setStudents(Set stud)

{

this.students=stud;

}

public Set getStudents()

{

return this.students;

}

}

Course持久对象在前一篇文章已经介绍,在这里就不再列举。Address持久对象比较简单,除了表字段定义的属性外,没有引入其它的属性,请参考本文的代码。

描述对象之间的关系

现在我们已经编写好了持久对象,下面的任务就是描述它们之间的关系。首先我们看Student持久对象的描述,如例程3所示。

例程3 Student持久对象的描述(Student.hbm.XML)

<?xml version="1.0"?>

<!DOCTYPE hibernate-mapping PUBLIC

"-//Hibernate/Hibernate Mapping DTD 2.0//EN"

"http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd">

<hibernate-mapping>

<class

name="com.hellking.study.hibernate.Student"

table="Students"

dynamic-update="false"

>

<!-- 描述ID字段-->

<id

name="id"

column="StudentId"

type="string"

unsaved-value="any"

>

<generator class="assigned"/>

</id>

<!-- 属性-->

<property

name="name"

type="string"

update="true"

insert="true"

column="Name"

/>

<!-- 描述Student和Course多对多的关系-->

<set

name="courses"

table="Student_Course_Link"

lazy="false"

inverse="false"

cascade="all"

sort="unsorted"

>

<key

column="StudentId"

/>

<many-to-many

class="com.hellking.study.hibernate.Course"

column="CourseId"

outer-join="auto"

/>

</set>

<!-- 描述Student和Classes之间多对一的关系-->

<many-to-one

name="classes"

class="com.hellking.study.hibernate.Classes"

cascade="none"

outer-join="auto"

update="true"

insert="true"

column="ClassesId"

/>

<!-- 描述Student和Address之间一对一的关系-->

<one-to-one

name="address"

class="com.hellking.study.hibernate.Address"

cascade="none"

outer-join="auto"

constrained="false"

/>

</class>

</hibernate-mapping>

在Student.hbm.xml描述符中,共描述了三种关系。第一种是Student和Address之间一对一的关系,它是最简单的关系,使用:

<one-to-one name="" class="">

来描述,这里的name表示的是Student对象中名称为address的属性;class表示的是address属性的类型:com.hellking.study.hibernate.Address。

接下来看Student和Classes之间多对一的关系,使用:

<many-to-one name="classes" class="com.hellking.study.hibernate.Classes" column="ClassesId" … />

来描述。同样,name表示的是Student对象中名称为classes的属性;class表示的是classes属性的类型,column表示Student表引用Classes表使用的外部键名称。对应的,在Classes类中也引用了Student类,它使用了以下的描述符来描述这个关系:

<set

name="students"

table="Students"

lazy="false"

inverse="false"

cascade="all"

sort="unsorted"

>

<key

column="ClassesId"

/>

<one-to-many

class="com.hellking.study.hibernate.Student"

/>

</set>

在描述Student和Course之间多对多关系时,使用了以下的方法:

<set

name="courses"

table="Student_Course_Link"

lazy="false"

inverse="false"

cascade="all"

sort="unsorted"

>

<key

column="StudentId"

/>

<many-to-many

class="com.hellking.study.hibernate.Course"

column="CourseId"

outer-join="auto"

/>

</set>

在映射多对多关系时,需要另外使用一个链接表,这个表的名字由table属性指定,链接表包含了两个字段:CourseId和StudentId。以下的描述:

<key column="StudentId">

指定了Student对象在Student_Course_Link链接表中的外部键。对应的,在Course持久对象使用了以下的描述符来描述这个关系:

<set

name="students"

table="Student_Course_Link"

lazy="false"

inverse="false"

cascade="all"

sort="unsorted"

>

<key

column="CourseId"

/>

<many-to-many

class="com.hellking.study.hibernate.Student"

column="StudentId"

outer-join="auto"

/>

</set>

由于其它持久对象的描述基本一样,在这里就不一一列举了,请参考本文的源代码。最后别忘了在hibernate.cfg.xml里增加这几个对象的描述。

<!-- Mapping files -->

<mapping resource="Address.hbm.xml"/>

<mapping resource="Student.hbm.xml"/>

<mapping resource="Classes.hbm.xml"/>

<mapping resource="Course.hbm.xml"/

使用映射关系

下面我们开发一个简单的实例来测试这个映射。持久对象使用最频繁的操作是增加数据、查询数据、删除数据、更新数据。对于更新数据的操作的情况,多个表的操作和单个表没有两样,在这里不举例了。

添加数据到数据库

我们在这里测试前三种操作,首先来看添加数据到数据库的情况,如例程4所示。

例程4 测试持久对象之间的映射关系之添加数据(MapTestBean.java部分代码)

/**

*在数据库中添加数据

*/

public void addData(String studentId,String classesId,String coursesId)

throws HibernateException {

try

{

/**

*以下的代码添加了一个Student,同时为Student指定了

*Address、Courses和Classses。

*/

beginTransaction();

//创建一个Student对象 。

Student student = new Student();

student.setName("hellking2");

student.setId(studentId);

//创建一个Address对象。

Address addr=new Address();

addr.setCity("beijing");

addr.setState("bj");

addr.setStreet("tsinghua");

addr.setZip("100083");

addr.setId(student.getId());

//设置Student和address的关系。

student.setAddress(addr);

Set set=new HashSet();

set.add(student);

//创建一个course对象。

Course course=new Course ();

course.setId(coursesId);

course.setName("computer_jsp");

//设置course和student对象之间的关系。

course.setStudents(set);

//创建一个classes对象。

Classes cl=new Classes();

cl.setId(classesId);

cl.setName("engine power");

//设置某个classes对象包含的students对象。

cl.setStudents(set);

//由于是双向的关系,student对象也需要设置一次。

student.setClasses(cl);

//保存创建的对象到session中。

session.save(cl);

session.save(course);

session.save(student);

session.save(addr);

//提交事务,使更改生效。

endTransaction(true);

}

catch(HibernateException e)

{

System.out.println("在添加数据时出错!");

e.printStackTrace();

throw e;

}

}

在例程4中,添加数据到数据库之前,首先设置持久对象的各个属性,如:

student.setName("hellking2");

这种设置属性的方式和普通的类没有什么区别,设置完所有的属性后,就设置持久对象之间的关系,如:

student.setAddress(addr);

假如存在对象之间的多重关系,那么可能需要把对象保存在Set集合中,然后再进行设置,如:

Set set=new HashSet();

set.add(student);

course.setStudents(set);

当设置完所有的属性和对象关系之后,就可以调用:

session.save(persistentObject);

方法把持久对象保存到Hibernate会话中。最后,调用endTransaction来提交事务,并且关闭Hibernate会话。

数据查询

在复杂的实体对象映射中,往往查询也比较复杂。作为演示,我们在这里也提供了几个查询方法,如例程5所示。

例程5 测试持久对象之间的映射关系之查询数据(MapTestBean.java部分代码)

/**

*获得某个给定studentid的Student的地址信息

*/

public Address getAddress(String id) throws HibernateException

{

beginTransaction();

Student st=(Student)session.load(Student.class,id);

Address addr=(Address)session.load(Address.class,st.getId());

endTransaction(false);

return addr;

}

/**

*获得某个给定studentid的Student的所有课程

*/

public Set getCourses(String id)throws HibernateException

{

beginTransaction();

Student st=(Student)session.load(Student.class,id);

endTransaction(false);

return st.getCourses();

}

/**

*测试获得某个给定studentid的Student所属的Classes

*/

public Classes getClasses(String id)throws HibernateException

{

beginTransaction();

Student st=(Student)session.load(Student.class,id);

System.out.println(st.getClasses().getId());

endTransaction(false);

return st.getClasses();

}

这里提供了三种查询方法,分别是:

查询给定id的Student的Address信息;

查询给定id的Student的所有Courses信息;

查询给定id的Student所属的Classes信息。

在查询时,首先使用beginTransaction()方法创建一个Hibernate会话对象,并且开始一个新Hibernate事务;然后通过session.load()方法获得给定ID的Student对象,如:

Student st=(Student)session.load(Student.class,id);

最后调用student.getXXX()方法返回指定的对象。

删除数据

在表的关系比较复杂时,要删除数据,往往存在级联删除的情况,由于级联删除的情况比较复杂,在这里就不举例了。假设我们要删除和某个给定id的student对象的所有相关的记录,就可以使用例程6所示的方法。

例程6 测试持久对象之间的映射关系之删除数据(MapTestBean.java部分代码)

/**

*删除和某个学生相关的所有信息

*(这里只是测试,我们暂且不说这种操作的意义何在)。

*/

public void delteStudent(String id)throws HibernateException

{

beginTransaction();

Student st=(Student)session.load(Student.class,id);

Address addr=(Address)session.load(Address.class,st.getId());

//删除address信息。

session.delete(addr);

//删除classes信息。

session.delete(st.getClasses());

/**

*逐个删除course。

*/

for(Iterator it=st.getCourses().iterator();it.hasNext();)

{

Course c=(Course)it.next();

session.delete(c);

}

//最后,删除student对象。

session.delete(st);

endTransaction(true);

}

同样,在执行删除前,首先使用beginTransaction()方法创建一个新Hibernate会话和一个新Hibernate事务,然后把要删除的对象Load进来,接下来调用session.delete()方法来删除指定对象。

假如要删除的是集合中的对象,那么可以通过一个迭代来逐个删除,如例程6中删除courses的方法。

测试

在这里提供了在JSP中调用MapTestBean进行测试的程序,具体代码见maptest.jsp文件。在进行测试前,请确保连接数据库的配置完好,并且每个持久对象的配置都没有错误。假如配置出现困难,请参考本文的源代码。

行动起来

经过两篇文章由浅入深的学习,希望能够起到抛砖引玉的作用,相信读者对Hibernate的熟悉已经有一个整体的把握。Hibernate由于它的易用性和良好的移植性等特点,逐渐在企业级应用开发中广泛使用。Hibernate官方网站提供了非常好的使用手册,您可以参考它。假如您并非精通JDBC并且不想学习它,不妨考虑使用Hibernate;假如您在使用实体Bean之类的持久框架碰到困难,也许Hibernate可以助你一臂之力!

参考资料

本文的源代码在这里 下载。

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有