前幾篇系列博客:
細談Hibernate(一)hibernate基本概念和體系結構
細談Hibernate(二)開發第一個hibernate基本詳解
細談Hibernate(三)Hibernate常用API詳解及源碼分析
細談Hibernate(四)Hibernate常用配置文件詳解
在前幾篇博客,我們初步對Hibernate有了一定的基礎性的認知了,也能夠簡單的用hibernate進行增刪改查,但hibernate真正的難度和精髓我們都還沒接觸到,其中最主要的關聯映射就是其中一個,這篇博客,我們就一起來看一下這個hibernate關聯映射。我們大家都知道,在域模型(實體域)中,關聯關係是類與類之間最普遍的關係,他是指通過一個對象持有另一個對象的實例根據UML語言,關係是有方向的。實質上關聯映射的本質:將關聯關係映射到數據庫,所謂的關聯關係是對象模型在內存中的一個或多個引用。搞清關聯映射的的關鍵就在於搞清實體之間的關係。下面我們首先來看一下具體什麼事關聯關係:
一:關聯關係
1.關聯關係的方向可分爲單向關聯和雙向關聯。
單向關聯:假設存在兩張表person表和address表,如果在應用的業務邏輯中,僅需要每個person實例能夠查詢得到其對應的Address實例,而Address實例並不需要查詢得到其對應的person實例;或者反之。
雙向關聯:既需要每個person實例能夠查詢得到其對應的Address實例,Address實例也需要查詢得到其對應的person實例。
2.關聯的數量,根據擁有被關聯對象的個數確定
多對一(many to one):如用戶和組 學生和班級
一對多(one to many):如用戶和電子郵件
多對多(many to many):如學生選課
一對一(one to one):如用戶和身份證
下面我們就開始講第一種關聯關係:一對多,一對多總共分爲:單向一對多,單向多對一,雙向一對多。這主要是站在關係雙方各自角度來定義的。下面我們就來一一來看一下
二.單向多對一
單向多對一關聯是最常見的單向關聯關係,如:多個用戶屬於同一個組,多個學生處於同一個班級。之所以叫他多對一而不是一對多,是因爲他們之間的關係是多的一方來維護的,下面我們就以多個用戶屬於同一個組來詳細說明一下單向多對一。首先看一下他們的關係示例:
從上邊的圖示中可以看出,多個用戶屬於一個組,我們用多的一方來維護,所以我們可以根據用戶可以知道他在哪個組,而不需要知道一個組裏有哪些學生,這就是所謂的單向的。多對一映射原理:在多的一端加入一個外鍵指向一的一端,它維護的關係多指向一,一對多映射原理,在多的一端加入一個外鍵指向一的一端,她維護的關係是一指向多,也就是說一對多與多對一的映射原理是一樣的,只是站的角度不一樣。下面來看一下單向多對一關係配置文件:
Group:一的一方,不需要維護關係,所以和普通配置一樣
- <hibernate-mapping>
- <class name="cn.edu.bzu.manytoone.entity.Group" table=“group">
- <id name="id">
- <column name="id" />
- <generator class="native" />
- </id>
- <property name="name" type="java.lang.String">
- <column name="name" length="50" not-null="true" />
- </property>
- </class>
- </hibernate-mapping>
User:多的一方,需要維護雙方關係,內有一的一方引用:
- <hibernate-mapping>
- <class name="cn.edu.bzu.manytoone.entity.User" table=“user”>
- <id name="id">
- <column name="id" />
- <generator class="native" />
- </id>
- <many-to-one name=“group” column =“group_id”/>
- </many-to-one>
- <property name="name" type="java.lang.String">
- <column name="street_name" length="50" not-null="true" />
- </property>
- </class>
- </hibernate-mapping>
注:1.many-to-one 元素的常用屬性:
具體的抓取數據策略會再以後詳細講解
2.重要屬性 - cascade(級聯)
級聯的意思是指定兩個對象之間的操作聯動關係,對一個對象執行了操作之後,對其指定的級聯對象也需要執行相同的操作
總共可以取值爲:all、none、save-update、delete
all-代表在所有的情況下都執行級聯操作
none-在所有情況下都不執行級聯操作
save-update-在保存和更新的時候執行級聯操作
delete-在刪除的時候執行級聯操作
三.單向一對多
所謂單向一對多,就是實體之間的關係由“一” 的一端加載“多” 的一端,關係由“一”的一端來維護,在JavaBean中是在“一”的一端中持有“多”的一端的集合,Hibernate把這種關係反映到數據庫的策略是在“多”的一端的表上加一個外鍵指向“一” 的一端的表的主鍵。比如Class(班級)和Student(學生)之間是一對多的關係。一個班級裏面有很多的學生,站在班級的角度上來看就是一個班級對應多個學生。我們來看一下具體的關係圖:
從圖上我們可以看出,在一的一端含有一個多的引用的集合,我們可以根據班級找到它有哪些學生,而不能根據學生找到他對應的班級。在一的一端維護的缺點:
* 如果將student表裏的classesid設爲非空,則無法保存;
* 因爲不是在student端維護數據,所以student端不知道學生是哪個班的
* 需要發出多餘的update語句來更新關係;
下面我們就具體來看一下一對多中維護關係的“一”中是怎麼樣來配置的:Class映射文件
- <hibernate-mapping>
- <class name="cn.edu.bzu.hibernate.Class" table=“tb_class”>
- <id name="id">
- <generator class=“native"/>
- </id>
- <property name="name"/>
- <set name="students" >
- <key column="classid" ></key>
- <one-to-many class=“cn.edu.bzu.hibernate.Student" />
- </set>
- </class>
- </hibernate-mapping>
注意:
1.<set>元素的inverse屬性:在映射一對多的雙向關聯時,應該在“one”方把inverse屬性設爲true,這樣可提高應用性能。
inverse:控制反轉,爲true表示反轉,由它方負責;反之,不反轉,自己負責;如果不設,one和many兩方都要負責控制,因此,會引發重複的sql語句以及重複添加數據,
2.級聯刪除(從數據庫刪除相關表記錄)
當刪除Customer對象時,及聯刪除Order對象.只需將cascad屬性設爲delete即可.
注:刪除後的對象,依然存在於內存中,只不過由持久化態變爲臨時態.
3.父子關係(邏輯刪除,只是解除了關聯關係)
自動刪除不再和Customer對象關聯的Order對象.只需將cascade屬性設爲delete-orphan.
注:當關聯雙方都存在父子關係,就可以把父方的cascade屬性設爲delete-orphan,所謂父子關係,是由父方來控制子方的生命週期.
4.<set name="students" >
<key column="classid" ></key>
<one-to-many class=“cn.edu.bzu.hibernate.Student" />
</set>
Name爲--持久化對象的集合的屬性名稱
<key column="classid" ></key>外鍵的名稱
<one-to-many class=“cn.edu.bzu.hibernate.Student" />持久化類
四:雙向一對多關聯
所謂雙向一對多關聯,同時配置單向一對多和單向多對一就成了雙向一對多關聯,上面兩種都是單向的,但是在實際開發過程中,很多時候都是需要雙向關聯的,它在解決單向一對多維護關係的過程中存在的缺陷起了一定的修補作用。在插入學生的時候,如果班級不能爲空,則學生是插入不了的。還有如果插入成功,在開始解決班級字段是空的,在事務提交階段,班級需要更新每一個學生的班級ID,這樣會產生大量的Update語句。影響效率。所以一對多關係大多使用雙向一對多映射。具體配置文件:
多的一方:Student映射文件
- <hibernate-mapping>
- <class name="cn.edu.bzu.hibernate.Student">
- <id name="id">
- <generator class=“native"/>
- </id>
- <property name="name"/>
- <many-to-one name=“classes” column=“classid”/>
- </class>
- </hibernate-mapping>
一的一方:Class映射文件
- <hibernate-mapping>
- <class name="cn.edu.bzu.hibernate.Class" table=“tb_class”>
- <id name="id">
- <generator class=“native"/>
- </id>
- <property name="name"/>
- <set name="students">
- <key column="classid" ></key>
- <one-to-many class=“cn.edu.bzu.hibernate.Student" />
- </set>
- </class>
- </hibernate-mapping>
注:
1.一對多雙向關聯映:
* 在一的一端的集合上使用<key>,在對方表中加入一個外鍵指向一的一端;
* 在多一端採用<many-to-one>
2.key標籤指定的外鍵字段必須和<many-to-one>指定的外鍵字段一致,否則引用字段錯誤;
3.如果在一的一端維護一對多關聯關係,hibernate會發出多餘的Update語句,多以我們一般在多的一端維護關聯關係
4.關於inverse屬性;
inverse主要用在一對多,多對對雙向關聯上,inverse可以設置到<set>集合上,
默認inverse爲false,所以我們可以從一的一端和多的一端來維護關聯關係,如果inverse爲true,我們只能從多的一端來維護關聯關係,注意:inverse屬性,隻影響存儲(使存儲方向轉變),即持久,
5.區分inverse和cascade
Inverse:負責控制關係,默認爲false,也就是關係的兩端都能控制,但這樣會造成一些問題,更新的時候會因爲兩端都控制關係,於是重複更新。一般來說有一端要設爲true。
Cascade:負責控制關聯對象的級聯操作,包括更新、刪除等,也就是說對一個對象進行更新、刪除時,其它對象也受影響,比如我刪除一個對象,那麼跟它是多對一關係的對象也全部被刪除。
舉例說明區別:刪除“一”那一端一個對象O的時候,如果“多”的那一端的Inverse設爲true,則把“多”的那一端所有與O相關聯的對象外鍵清空;如果“多”的那一端的Cascade設爲Delete,則把“多”的那一端所有與O相關聯的對象全部刪除。