4.3 Hibernate關係映射

    Hibernate關係映射的主要任務是實現數據庫關係表與持久化類之間的映射java

1.一對一關聯

    有兩種實現方式:共享主鍵方式使兩個數據表的主鍵使用相同的值,經過主鍵造成一對一映射關係)和惟一外鍵方式一個表的外鍵與另外一個表的惟一主鍵對應造成一對一映射關係)。sql

1)共享主鍵方式

    在註冊某個論壇會員的時候,每每不但要填寫登陸帳號和密碼,還要填寫其餘的詳細信息,這兩部分信息一般會放在不一樣的表中,如表4.一、表4.2所示。數據庫

字 段 名 稱session

數 據 類 型app

    ide

    測試

允 許 爲 空this

    spa

ID.net

int(4)



ID

USERNAME

varchar(20)




登陸帳號

PASSWORD

varchar(20)




登陸密碼

表4.1  登陸表Login 

字 段 名 稱

數 據 類 型

    

    

允 許 爲 空

    

ID

int(4)

1


ID

TRUENAME

varchar(8)



真實姓名

EMAIL

varchar(50)



電子郵件

表4.2  詳細信息表Detail 

        登陸表和詳細信息表屬於典型的一對一關聯關係,可按共享主鍵方式進行。步驟以下:

(1) 建立Java項目,命名爲「Hibernate_mapping」。 

(2)添加Hibernate開發能力,步驟同4.2.1節第4步。HibernateSessionFactory類一樣位於org.util包下。(注意先要配置數據庫鏈接) 

(3)編寫生成數據庫表對應的Java類對象和映射文件。(注意放置的位置:選對項目名)   

 a)Login表對應的POJO類Login.java:

package org.model;
public class Login implements java.io.Serializable{
            private int id;                                               // ID號
            private String username;                              // 登陸帳號
            private String password;                              // 密碼
            private Detail detail;                                 // 詳細信息
            // 省略上述各屬性的getter和setter方法
}


b)Detail表對應的Detail.java:

package org.model;
public class Detail implements java.io.Serializable{
           private int id;                                          // ID號
           private String trueName;                         // 真實姓名
           private String email;                             // 電子郵件
           private Login login;                             // 登陸信息
           // 省略上述各屬性的getter和setter方法
}


 c)Login表與Login類的ORM映射文件Login.hbm.xml。

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-mapping
  PUBLIC '-//Hibernate/Hibernate Mapping DTD 3.0//EN'
  'http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd'>
<hibernate-mapping>
  <!-- name指定POJO類,table指定對應數據庫的表 -->
  <class name="org.model.Login" table="login">
    <!-- name指定主鍵,type指定主鍵類型 -->
    <id name="id" type="java.lang.Integer">
      <column length="4" name="ID"/>
      <!-- 採用foreign標誌生成器,直接採用外鍵的屬性值,達到共享主鍵的目的-->
      <generator class="foreign">
        <param name="property">detail</param>
      </generator>
    </id>
    <!-- POJO屬性及表中字段的對應 -->
    <property name="username" type="java.lang.String">
      <column name="USERNAME"/>
    </property>
    <property name="password" type="java.lang.String">
      <column name="PASSWORD"/>
    </property>
    <!-- name表示屬性名字,class表示被關聯的類的名字,constrained="true"代表當前的主鍵上存在一個外鍵約束-->
    <one-to-one class="org.model.Detail" constrained="true" name="detail"/>
  </class>
</hibernate-mapping>


d)Detail表與Detail類的ORM映射文件Detail.hbm.xml:

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-mapping
  PUBLIC '-//Hibernate/Hibernate Mapping DTD 3.0//EN'
  'http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd'>
<hibernate-mapping>
  <!-- name指定POJO類,table指定對應數據庫的表 -->
  <class name="org.model.Detail" table="detail">
    <!-- name指定主鍵,type指定主鍵類型 -->
    <id name="id" type="java.lang.Integer">
      <column length="4" name="ID"/>
      <generator class="identity"/>
    </id>
    <!-- POJO屬性及表中字段的對應 -->
    <property name="trueName" type="java.lang.String">
      <column name="TRUENAME"/>
    </property>
    <property name="email" type="java.lang.String">
      <column name="EMAIL"/>
    </property>
    <!-- name表示屬性名字,class表示被關聯的類的名字,cascade="all"代表主控類的全部操做,對關聯類也執行一樣操做,lazy="false"表示此關聯爲當即加載-->
    <one-to-one cascade="all" class="org.model.Login" lazy="false" name="login"/>
  </class>
</hibernate-mapping>


(4)在hibernate.cfg.xml文件中加入配置映射文件的語句。

<mapping resource="org/model/Detail.hbm.xml"/>
<mapping resource="org/model/Login.hbm.xml"/>

5)建立測試類。    在src文件夾下建立包test,在該包下創建測試類,命名爲「Test.java」。其代碼。

package test;
 
import java.util.List;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.model.*;
import org.util.HibernateSessionFactory;
import java.sql.*;
 
public class Test {
    public static void main(String[] args) {
        // 調用HibernateSessionFactory的getSession方法建立Session對象
        Session session = HibernateSessionFactory.getSession();
        // 建立事務對象
        Transaction ts = session.beginTransaction();
        Detail detail = new Detail();
        Login login = new Login();
        login.setUsername("yanhong");
        login.setPassword("123");
        detail.setTrueName("嚴紅");
        detail.setEmail("yanhong@126.com");
        // 相互設置關聯
        login.setDetail(detail);
        detail.setLogin(login);
        // 這樣完成後就能夠經過Session對象調用session.save(detail)來持久化該對象
        session.save(detail);
        ts.commit();
        HibernateSessionFactory.closeSession();
    }
}


(6) 運行程序,測試結果。    由於該程序爲Java Application,因此能夠直接運行。在徹底沒有操做數據庫的狀況下,程序就完成了對數據的插入。插入數據後,Login表和Detail表的內容如圖4.十二、圖4.13所示。

圖4.12  Login表

圖4.13  Detail表 

2)惟一外鍵方式

    惟一外鍵的狀況不少,例如,每一個人對應一個房間。其實在不少狀況下,能夠是幾我的住在同一個房間裏面,就是多對一的關係。可是若是把這個多變成惟一,也就是說讓一我的住一個房間,就變成了一對一的關係了,這就是前面說的一對一的關係實際上是多對一關聯關係的一種特殊狀況。對應的Person表和Room表如表4.三、表4.4所示。

字 段 名 稱

數 據 類 型

    

    

允 許 爲 空

    

Id

int

1


ID

name

varchar(20)




姓名

room_id

int



房間號

表4.3  Person表

字 段 名 稱

數 據 類 型

    

    

允 許 爲 空

    

id

int(4)

1


ID

address

varchar(100)




地址

表4.4  Room表  

步驟以下:

(1)在項目Hibernate_mapping的org.model包下編寫生成數據庫表對應的Java類對象和映射文件。    

Person表對應的POJO類Person.java:

package org.model;
public class Person implements java.io.Serializable {
    private Integer id;
    private String name;
    private Room room;
    // 省略上述各屬性的getter和setter方法
}


Room表對應的POJO類Room.java:

package org.model;
public class Room implements java.io.Serializable{
    private int id; 
    private String address;
    private Person person;
    // 省略上述各屬性的getter和setter方法
}


Person表與Person類的ORM映射文件Person.hbm.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN" 
    "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping> 
    <class name="org.model.Person" table="Person"> 
        <id name="id" column="id" type="java.lang.Integer"> 
            <generator class="native"/> 
        </id> 
        <property name="name" column="name" type="java.lang.String"/>         
        <many-to-one name="room" column="room_id" class="org.model.Room"
            cascade="all" unique="true" />
       <!--     name="room"                    屬性名稱
                column="room_id"               充當外鍵的字段名
                class="org.model.Room"         被關聯的類的名稱
                cascade="all"                  主控類全部操做,對關聯類也執行一樣操做
                unique="true"                  惟一性約束,實現一對一  --> 
    </class> 
</hibernate-mapping>

去掉roomId的配置

<property name="roomId" type="java.lang.Integer">
    <column name="room_id" />
</property>

Room表與Room類的ORM映射文件Room.hbm.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN" 
    "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
    <class name="org.model.Room" table="Room"> 
        <id name="id" column="id"> 
            <generator class="native"/> 
        </id>                                                        
        <property name="address" column="address" type="java.lang.String"/>             
        <one-to-one name="person"                  
              class="org.model.Person"                
              property-ref="room"/>                 
        <!--   name="person"                   屬性名
               class="org.model.Person"        被關聯的類的名稱
               property-ref="room"             指定關聯類的屬性名      -->   
    </class>
</hibernate-mapping>

(2)在hibernate.cfg.xml文件中加入以下的配置映射文件的語句。

<mapping resource="org/model/Person.hbm.xml"/>
<mapping resource="org/model/Room.hbm.xml"/>


(3)編寫測試代碼。    

在src文件夾下的包test的Test類中加入以下代碼:

Person person=new Person();
person.setName("liumin");
Room room=new Room();
room.setAddress("NJ-S1-328");
person.setRoom(room);
session.save(person);


4)運行程序,測試結果。    

由於該程序爲Java Application,因此能夠直接運行。在徹底沒有操做數據庫的狀況下,程序就完成了對數據的插入。插入數據後,Person表和Room表的內容如圖4.1四、圖4.15所示。

圖4.14  Person表

 

圖4.15  Room表    

2.多對一單向關聯

    只要把上例中的一對一的惟一外鍵關聯實例稍微修改就能夠變成多對一。步驟以下: 

(1)在項目Hibernate_mapping的org.model包下編寫生成數據庫表對應的Java類對象和映射文件

其對應表不變,Person表對應的類也不變,對應的Person.hbm.xml文件修改以下(只是去掉 <many-to-one/>中的unique="true"):

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN" 
    "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping> 
    <class name="org.model.Person" table="Person"> 
        <id name="id" column="id" type="java.lang.Integer"> 
            <generator class="native"/> 
        </id> 
        <property name="name" column="name" type="java.lang.String"/>         
        <many-to-one name="room"                  
                     column="room_id"                 
                     class="org.model.Room"         
                     cascade="all"/>        
    </class> 
</hibernate-mapping>


Room表不變,對應的POJO類以下:(去掉private Person person及其方法;)

package org.model;
public class Room implements java.io.Serializable{
    private int id; 
    private String address;
    // 省略上述各屬性的getter和setter方法
}

Room表與Room類的ORM映射文件Room.hbm.xml以下(去掉<one-to-one/>配置):

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN" 
    "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping> 
    <class name="org.model.Room" table="room"> 
        <id name="id" column="id"> 
            <generator class="native"/> 
        </id> 
        <property name="address" 
                  column="address" 
                  type="java.lang.String"/>
    </class> 
</hibernate-mapping>

由於是單向多對一,因此無需在「一」的一邊指定「多」的一邊。(比如一班學生能夠記住一個老師,這個老師記不住每一個學生)

(2)編寫測試代碼。    

在src文件夾下的包test的Test類中加入以下代碼(跟上例同樣,能夠修改一下數據):

Person person=new Person();
person.setName("liuyanmin");
Room room=new Room();
room.setAddress("NJ-S1-328");
person.setRoom(room);
session.save(person);

(3) 運行程序,測試結果。    

由於該程序爲Java Application,因此能夠直接運行。在徹底沒有操做數據庫的狀況下,程序就完成了對數據的插入。插入數據後,Person表和Room表的內容如圖4.1六、圖4.17所示。

圖4.16  Person表 

圖4.17  Room表 

3.一對多雙向關聯

    多對一單向關聯,「一」的一方也知道「多」的一方,就變成了一對多(或多對一)雙向關聯下面經過修改4.3.2節的例子來完成雙向多對一的實現。步驟以下: 

(1) 在項目Hibernate_mapping的org.model包下編寫生成數據庫表對應的Java類對象和映射文件。    

Person表對應的POJO及其映射文件不用改變,如今來修改Room表對應的POJO類及其映射文件。對應的POJO類Room.java。

package org.model;
import java.util.HashSet;
import java.util.Set;
public class Room {
    private int id;
    private String address;
    private Set person=new HashSet();  //集合,存放多個Person對象
        public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id=id;
    }
    public String getAddress() {
        return address;
    }
 
    public void setAddress(String address) {
        this.address=address;
    }
    public Set getPerson() {
        return person;
    }
    public void setPerson(Set person) {
        this.person=person;
    }
}


Room表與Room類的ORM映射文件Room.hbm.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN" 
    "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping> 
    <class name="org.model.Room" table="room"> 
        <id name="id" column="id"> 
            <generator class="native"/> 
        </id> 
        <property name="address" column="address" type="java.lang.String"/>
        <set name="person"            
                 inverse="false"            
                 cascade="all">             
             <key column ="room_id" />                      
             <one-to-many class="org.model.Person" />       
        <!--      name="person"             此屬性爲Set集合類型,由name指定屬性名字
                 inverse="false"           表示關聯關係的維護工做由誰來負責,默認false,表示由主控方負責;true表示由被控方負責。因爲該例是雙向操做,故須要設爲false,也可不寫
                 cascade="all"             級聯程度
             key column ="room_id"                      充當外鍵的字段名
             one-to-many class="org.model.Person"       被關聯的類名字 -->
      </set>
    </class> 
</hibernate-mapping>


該配置文件中cascade配置的是級聯程度,它有如下幾種取值:

  • all:表示全部操做句在關聯層級上進行連鎖操做。

  • save-update:表示只有save和update操做進行連鎖操做。

  • delete:表示只有delete操做進行連鎖操做。

  • all-delete-orphan:在刪除當前持久化對象時,它至關於delete;在保存或更新當前持久化對象時,它至關於save-update。另外它還能夠刪除與當前持久化對象斷開關聯關係的其餘持久化對象。

(2)編寫測試代碼。在src文件夾下的包test的Test類中加入以下代碼:

Person person1=new Person();
Person person2=new Person();
Room room=new Room();
room.setAddress("NJ-S1-328");
person1.setName("李方方");
person2.setName("王豔");
person1.setRoom(room);
person2.setRoom(room);
//這樣完成後就能夠經過Session對象調用session.save(person1)和session.save(person)會自動保存room
session.save(person1);
session.save(person2);


(3)運行程序,測試結果。    由於該程序爲Java Application,因此能夠直接運行。在徹底沒有操做數據庫的狀況下,程序就完成了對數據的插入。插入數據後,Person表和Room表的內容如圖4.1八、圖4.19所示。


圖4.18  Person表

圖4.19  Room表 

因爲是雙向的,固然也能夠從Room的一方來保存Person,在Test.java中加入以下代碼:

Person person1=new Person();
Person person2=new Person();
Room room=new Room();
room.setAddress("NJ-S1-328");
person1.setName("李方方");
person2.setName("王豔");
Set persons=new HashSet();
persons.add(person1);
persons.add(person2);
room.setPerson(persons);
//這樣完成後,就能夠經過Session對象調用session.save(room)會自動保存person1和person2
session.save(room);

運行程序,插入數據後,Person表和Room表的內容如圖4.20、圖4.21所示。

圖4.20  Person表

 

圖4.21  Room表 

4.多對多關聯

1)多對多單向關聯

    學生和課程就是多對多的關係,一個學生能夠選擇多門課程,而一門課程又能夠被多個學生選擇。多對多關係在關係數據庫中不能直接實現,還必須依賴一張鏈接表。如表4.六、表4.7和表4.8所示。

字 段 名 稱

數 據 類 型

主    鍵

自    增

允 許 爲 空

描    述

ID

int

增1


ID號

SNUMBER

varchar(10)




學號

SNAME

varchar(10)



姓名

SAGE

int



年齡

表4.6  學生表student 

字 段 名 稱

數 據 類 型

主    鍵

自    增

允 許 爲 空

描    述

ID

int

增1


ID號

CNUMBER

varchar(10)




課程號

CNAME

varchar(20)



課程名

表4.7  課程表course 

字 段 名 稱

數 據 類 型

主    鍵

自    增

允 許 爲 空

描    述

SID

int



學生ID號

CID

int



課程ID號

表4.8  鏈接表stu_cour     

因爲是單向的也就是說從一方能夠知道另外一方,反之不行。這裏以從學生知道選擇了哪些課程爲例實現多對多單向關聯。步驟以下:

(1)在項目Hibernate_mapping的org.model包下編寫生成數據庫表對應的Java類對象和映射文件。student表對應的POJO類以下:

package org.model;
import java.util.HashSet;
import java.util.Set;
 
public class Student implements java.io.Serializable{
private int id;
private String snumber;
private String sname;
private int sage;
private Set courses=new HashSet();
 //省略上述各屬性的getter和setter方法
}


student表與Student類的ORM映射文件Student.hbm.xml

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="org.model.Student" table="student">
        <id name="id" type="java.lang.Integer">
            <column name="ID" length="4" />
            <generator class="identity"/>
        </id>
        <property name="snumber" type="java.lang.String">
            <column name="SNUMBER"/>
        </property>
        <property name="sname" type="java.lang.String">
            <column name="SNAME" />
        </property>
        <property name="sage" type="java.lang.Integer">
            <column name="SAGE"></column>
        </property>
 
        <set name="courses"           // set標籤表示此屬性爲Set集合類型,由name指定屬性名稱
                table="stu_cour"      // 鏈接表的名稱
                lazy="true"           // 表示此關聯爲延遲加載,所謂延遲加載就是到了用的時候進行加載,避免大量暫時無用的關係對象
                cascade="all">        // 級聯程度
            <key column="SID"></key>                          // 指定參照student表的外鍵名稱
            <many-to-many class="org.model.Course"            // 被關聯的類的名稱
                          column="CID"/>                      // 指定參照course表的外鍵名稱
        </set>
    </class>
</hibernate-mapping>

course表對應的POJO以下:

package org.model;
public class Course implements java.io.Serializable{
private int id;
           private String cnumber;
           private String cname;
           //省略上述各屬性的getter和setter方法。
}

course表與Course類的ORM映射文件Course.hbm.xml

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
    <class name="org.model.Course" table="course">
        <id name="id" type="java.lang.Integer">
            <column name="ID" length="4" />
            <generator class="identity"/>
        </id>
 
        <property name="cnumber" type="java.lang.String">
            <column name="CNUMBER"/>
        </property>
 
        <property name="cname" type="java.lang. String ">
            <column name="CNAME" />
        </property>
    </class>
</hibernate-mapping>


(2)在hibernate.cfg.xml文件中加入以下的配置映射文件的語句。

<mapping resource="org/model/Student.hbm.xml"/>
<mapping resource="org/model/Course.hbm.xml"/>


(3)編寫測試代碼。在src文件夾下的包test的Test類中加入以下代碼。

Course cour1=new Course();
Course cour2=new Course();
Course cour3=new Course();
cour1.setCnumber("101");
cour1.setCname("計算機基礎");
cour2.setCnumber("102");
cour2.setCname("數據庫原理");
cour3.setCnumber("103");
cour3.setCname("計算機原理");
Set courses=new HashSet();
courses.add(cour1);
courses.add(cour2);
courses.add(cour3);
Student stu=new Student();
stu.setSnumber("081101");
stu.setSname("李方方");
stu.setSage(21);
stu.setCourses(courses);
session.save(stu);
//設置完成後就能夠經過Session對象調用session.save(stu)完成持久化


(4) 運行程序,測試結果。    由於該程序爲Java Application,因此能夠直接運行。在徹底沒有操做數據庫的狀況下,程序就完成了對數據的插入。插入數據後,student表、course表及鏈接表stu_cour表的內容如圖4.2二、圖4.2三、圖4.24所示。

圖4.22  student表

圖4.23  course表

圖4.24  stu_cour表 

2)多對多雙向關聯

    同時實現 兩個互逆的多對多單項關聯便可    首先將其Course表所對應的POJO對象修改爲以下代碼:

package org.model;
import java.util.HashSet;
import java.util.Set;
public class Course implements java.io.Serializable{
           private int id;
           private String cnumber;
           private String cname;
           private Set stus=new HashSet();
           //省略上述各屬性的getter和setter方法
}

Course表與Course類的ORM映射文件Course.hbm.xml

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
    <class name="org.model.Course" table="course">
        <id name="id" type="java.lang.Integer">
            <column name="ID" length="4" />
            <generator class="identity"/>
        </id>
        <property name="cnumber" type="java.lang.String">
            <column name="CNUMBER"/>
        </property>
        <property name="cname" type="java.lang.String">
            <column name="CNAME" />
        </property>
        <set name="stus"                // set標籤表示此屬性爲Set集合類型,由name指定一個屬性名稱
                table="stu_cour"                       // 鏈接表的名稱
                lazy="true"                            // 關聯爲延遲加載
                cascade="all">                         // 級聯操做爲全部
             <key column="CID"></key>        // 指定參照course表的外鍵名稱
             <many-to-many class="org.model.Student"        // 被關聯的類名
                column="SID"/>                        // 指定參照student表的外鍵名稱
        </set>
    </class>
</hibernate-mapping>

實際用法與多對多單項關聯相同,只是主控方不一樣而已


附:目錄《JavaEE基礎實用教程》筆記說明

相關文章
相關標籤/搜索