您的位置:首页 > 其它

Hibernate中实现复杂的数据映射

2007-07-26 16:18 295 查看
在这个例子中,将考虑到表之间的一对一、一对多、多对多的情况。如图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所示。


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所示。


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所示。


<?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所示。




/** *//**


*在数据库中添加数据


*/


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所示。




/** *//**


*获得某个给定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的方法。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: