![]()
內容:
案例介紹創建Hibernate持久對象描述對象之間的關系使用映射關系測試行動起來參考資料關于作者
相關內容:
TCP/IP 介紹TCP/IP 介紹 -->
java 專區中還有:
教學工具與產品代碼與組件所有文章實用技巧![]()
![]()
陳亞強 (cyqcims@mail.tsinghua.edu.cn)
高級軟件工程師
2003 年 10 月
在前一篇文章《使用Hibernate來操作持久對象》中,介紹了Hibernate的基本概念,然后用實例演示了怎么在Web應用中使用Hibernate來封裝持久數據對象。然而在現實的項目中,我們往往需要操作多個數據表,并且多個表之間往往存在復雜的關系,在本文,將介紹怎么在Hibernate中描述多個表的映射關系,并且演示怎么操作關系復雜的持久對象。
閱讀本文前您需要以下的知識和工具:
本文的參考資料見參考資料
本文的全部代碼在這里下載
案例介紹
在第一篇文章中,我們對一個表進行了簡單的封裝。在這篇文章中,我們討論更加復雜的情況。
在這個例子中,將考慮到表之間的一對一、一對多、多對多的情況。如圖1所示。
在上面的數據模型圖中,Student是所有表的核心,它和Classes表是一對多的關系,和Course表是多對多的關系(通過Student_Course_Link表來鏈接),和Address表是一對一的關系。
通過分析,我們可以把上面的數據模型轉換成如下的Java持久對象,如圖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.setZjsp"); //設置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(); } 這里提供了三種查詢方法,分別是:
在查詢時,首先使用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可以助你一臂之力!
參考資料
本文的源代碼在這里 下載。
新聞熱點
疑難解答