在前一篇文章
《使用Hibernate來實現持久對象》中,介紹了Hibernate的基本概念,然后用實例演示了怎么在Web應用中使用Hibernate來封裝持久數據對象。然而在現實的項目中,我們往往需要操作多個數據表,并且多個表之間往往存在復雜的關系,在本文,將介紹怎么在Hibernate中描述多個表的映射關系,并且演示怎么操作關系復雜的持久對象。
本文的全部代碼在這里下載
案例介紹
在第一篇文章中,我們對一個表進行了簡單的封裝。在這篇文章中,我們討論更加復雜的情況。
在這個例子中,將考慮到表之間的一對一、一對多、多對多的情況。如圖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可以助你一臂之力!
posted on 2006-06-26 09:28
水煮三國 閱讀(152)
評論(0) 編輯 收藏