Hibernate利用關聯關係操縱對象

Hibernate利用關聯關係操縱對象

 

        數據對象之間關聯關係有一對1、一對多及多對多關聯關係。在數據庫操做中,數據對象之間的關聯關係使用JDBC處理很困難。本節講解如何在Hibernate中處理這些對象之間的關聯關係。本節使用到4個類,它們分別是Student(學生)、Card(學生證)、Group(班級)和Course(課程),它們之間的關聯關係如圖1-1所示。這些實體存在級聯(cascade)問題。例如,當刪除一個班級的信息時,還要刪除該班的全部學生的基本信息。若是直接使用JDBC執行這種級聯操做,會很是煩瑣。Hibernate經過把實體對象之間關聯關係及級聯關係在映射文件中聲明,比較簡便地解決了這類級聯操做問題。 java

圖1-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 session

< ?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,則表示當即加載。下面對當即加載和延遲加載這兩個概念進行說明。 app

  • 當即加載:表示Hibernate在從數據庫中取得數據,組裝好一個對象(好比學生1)後,會當即再從數據庫取得數據,組裝此對象所關聯的對象(例如學生證1)。
  • 延遲加載:表示Hibernate在從數據庫中取得數據,組裝好一個對象(好比學生1)後,不會當即再從數據庫取得數據,組裝此對象所關聯的對象(例如學生證1),而是等到須要時,纔會從數據庫取得數據,組裝此關聯對象。

        <one-to-one>元素的cascade屬性代表操做是否從父對象級聯到被關聯的對象,它的取值以下。 eclipse

  • none:在保存、刪除或修改對象時,不對其附屬對象(關聯對象)進行級聯操做。這是默認設置。
  • save-update:在保存、更新當前對象時,級聯保存、更新附屬對象(臨時對象、遊離對象)。
  • delete:在刪除當前對象時,級聯刪除附屬對象。
  • all:在全部狀況下均進行級聯操做,即包含save-update和delete操做。
  • delete-orphan:刪除和當前對象解除關係的附屬對象。

        <one-to-one>元素的fetch屬性的可選值是join和select,默認值是select。 測試

  • join:表示鏈接抓取(Join fetching) : Hibernate經過 在SELECT語句使用OUTER JOIN(外鏈接)來得到對象的關聯實例或者關聯集合。
  • select:表示查詢抓取(Select fetching):須要另外發送一條 SELECT 語句抓取當前對象的關聯實體或集合。

        例程1-3中<one-to-one>元素的cascade屬性設置爲「all」,表示增長、刪除及修改Student對象時,都會級聯增長、刪除和修改Card對象。 fetch

例程1-3 Card.hbm.xml ui

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

2、一對多關聯關係的使用

        一對多關係很常見,例如父親和孩子、班級與學生的關係就是很好的一對多的關係。在實際編寫程序時,一對多關係有兩種實現方式:單向關聯和雙向關聯。單向的一對多關係只需在一方進行映射配置,而雙向的一對多須要在關聯的雙方進行映射配置。下面以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-one name="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的動做,其實就是完成一個「學生添加到班級」的動做。

3、多對多關聯關係的使用

        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表沒有任何變化。

相關文章
相關標籤/搜索