Hibernate4實戰 之 第四部分:關系映射 – JAVA編程語言程序開發技術文章

數據表之間的關系分為三類:一對一、一對多、多對多
一對一數據表(部門表  和  部門主管表)
一對多數據表(部門表  和  部門下的人員表)
多對多數據表(部門表  和  人員表)
 

根據相互尋找的關系又分:單向和雙向
對象一對一 (雙向)

java代碼:
1. public class A { 
2. private B b = null;  } 
3. public class B { 
4. private A a = null;  } 
5. n對象一對多 (雙向) 
6. public class A { 
7. private B b = null;  } 
8. public class B { 
9. private Collection<A> colA = null;  } 
10. n對象多對多 (雙向) 
11. public class A { 
12. private Collection<B> colB = null;  } 
13. public class B { 
14. private Collection<A> colA = null;  } 
 
雙向一對多是最常用的映射關系
<key>元素
<key> 元素在父映射元素定義瞭對新表的連接,並且在被連接表中定義瞭一個外鍵引用原表的主鍵的情況下經常使用。

java代碼:
1. <key 
2. column="columnname"(1) 
3. on-delete="noaction|cascade"(2) 
4. property-ref="propertyName"(3) 
5. not-null="true|false"(4) 
6. update="true|false"(5) 
7. unique="true|false"(6) 
8. /> 
(1)column(可選):外鍵字段的名稱。也可以通過嵌套的 <column> 指定。
(2)on-delete(可選,默認是 noaction):表明外鍵關聯是否打開數據庫級別的級聯刪除。
(3)property-ref(可選):表明外鍵引用的字段不是原表的主鍵(提供給遺留數據)。
(4)not-null(可選):表明外鍵的字段不可為空,意味著無論何時外鍵都是主鍵的一部分。
(5)update(可選):表明外鍵決不應該被更新,這意味著無論何時外鍵都是主鍵的一部分。
(6)unique(可選):表明外鍵應有唯一性約束,這意味著無論何時外鍵都是主鍵的一部分。
對那些看重刪除性能的系統,推薦所有的鍵都應該定義為 on-delete="cascade",這樣Hibernate 將使用數據庫級的 ON CASCADE DELETE 約束,而不是多個 DELETE 語句用於映射集合類的
Hibernate映射元素取決於接口的類型。比如,<set>元素用來映射Set類型的屬性:

java代碼:
1. <class name="Product"> 
2. <id name="serialNumber" column="productSerialNumber"/> 
3. <set name="parts"> 
4. <key column="productSerialNumber" not-null="true"/> 
5. <one-to-many class="Part"/> 
6. </set> 
7. </class> 
除瞭<set>,還有<list>, <map>, <bag>, <array> 和 <primitive-array> 映射元素。<map>具有代表性 ,如下:

java代碼:
1. <map 
2. name="propertyName" (1) 
3. table="table_name" (2) 
4. schema="schema_name" (3) 
5. lazy="true|extra|false" (4) 
6. inverse="true|false" (5) 
7. cascade=“all|none|save-update|delete|all-delete-orphan|delete-orphan”(6) sort="unsorted|natural|comparatorClass" (7) 
8. order-by="column_name asc|desc" (8) 
9. where="arbitrary sql where condition" (9) 
10. fetch="join|select|subselect" (10) 
11. batch-size="N" (11) 
12. access="field|property|ClassName" (12) 
13. optimistic-lock="true|false" (13) 
14. mutable="true|false" (14) 
15. > 
16. <key …. /> <map-key …. /> 
17. <element …. /> 
18. </map> 
(1) name 集合屬性的名稱
(2) table (可選——默認為屬性的名稱)這個集合表的名稱(不能在一對多的關聯關系中使用)
(3) schema (可選) 表的schema的名稱, 他將覆蓋在根元素中定義的schema
(4) lazy (可選–默認為true) 可以用來關閉延遲加載(false),
(5) inverse (可選——默認為false) 標記這個集合作為雙向關聯關系中的方向一端。
(6) cascade (可選——默認為none) 讓操作級聯到子實體
(7) sort(可選)指定集合的排序順序
(8) order-by (可選, 僅用於jdk1.4) 指定表的字段(一個或幾個)再加上asc或者desc(可選), 定義Map,Set和Bag的迭代順序
(9) where (可選) 指定任意的SQL where條件, 該條件將在重新載入或者刪除這個集合時使用(當集合中的數據僅僅是所有可用數據的一個子集時這個條件非常有用)
(10) fetch (可選, 默認為select) 用於在外連接抓取、通過後續select抓取和通過後續subselect抓取之間選擇。
(11) batch-size (可選, 默認為1) 指定通過延遲加載取得集合實例的批處理塊大小
(12) access(可選-默認為屬性property):Hibernate取得集合屬性值時使用的策略
(13) 樂觀鎖 (可選 – 默認為 true): 對集合的狀態的改變會是否導致其所屬的實體的版本增長。 (對一對多關聯來說,關閉這個屬性常常是有理的)
(14) mutable(可變) (可選 – 默認為true): 若值為false,表明集合中的元素不會改變(在某些情況下可以進行一些小的性能優化)。
集合外鍵
集合實例在數據庫中依靠持有集合的實體的外鍵加以辨別。此外鍵作為集合關鍵字段加以引用。集合關鍵字段通過 <key> 元素映射。
在外鍵字段上可能具有非空約束。對於大多數集合來說,這是隱含的。對單向一對多關聯來說,外鍵字段默認是可以為空的,因此你可能需要指明 not-null=“true”。示例如下:

java代碼:
1. <key column="productSerialNumber" not-null="true"/> 
外鍵約束可以使用 ON DELETE CASCADE,示例如下:

java代碼:
1. <key column="productSerialNumber" on-delete="cascade"/> 
2. one-to-one 
通過 one-to-one 元素,可以定義持久化類的一對一關聯。

java代碼:
1. <one-to-one 
2. name="propertyName"(1) 
3. class="ClassName"(2) 
4. cascade="cascade_style"(3) 
5. constrained="true|false"(4) 
6. fetch="join|select"(5) 
7. property-ref="propertyNameFromAssociatedClass"(6) 
8. access="field|property|ClassName"(7) 
9. formula="any SQL expression"(8) 
10. lazy="proxy|no-proxy|false"(9) 
11. entity-name="EntityName"(10) 
12. /> 
(1)name:屬性名。
(2)class(可選 — 默認是通過反射得到的屬性類型):被關聯的類的名字。
(3)cascade(級聯)(可選)表明操作是否從父對象級聯到被關聯的對象。
(4)constrained(約束)(可選)表明該類對應的表對應的數據庫表,和被關聯的對象所對應的數據庫表之間,通過一個外鍵引用對主鍵進行約束。這個選項影響 save() 和 delete() 在級聯執行時的先後順序以及決定該關聯能否被委托(也在 schema export tool 中被使用)。
(5)fetch(可選 — 默認為 select):在外連接抓取(outer-join fetching)和序列選擇抓取(sequential select fetching)兩者中選擇其一。
(6)property-ref:(可選)指定關聯類的屬性名,這個屬性將會和本類的主鍵相對應。如果沒有指定,會使用對方關聯類的主鍵。
(7)access(可選 — 默認為 property):Hibernate 用來訪問屬性值的策略。
(8)formula (可選):絕大多數一對一的關聯都指向其實體的主鍵。在一些少見的情況中,你可能會指向其他的一個或多個字段,或者是一個表達式,這些情況下,你可以用一個 SQL 公式來表示。(可以在 org.hibernate.test.onetooneformula 找到例子)
(9)lazy(可選 — 默認為 proxy):默認情況下,單點關聯是經過代理的。lazy="no-proxy"指定此屬性應該在實例變量第一次被訪問時應該延遲抓取(fetche lazily)(需要運行時字節碼的增強)。 lazy="false"指定此關聯總是被預先抓取。註意,如果constrained="false", 不可能使用代理,Hibernate會采取預先抓取。
(10)entity-name(可選):被關聯的類的實體名。
nmany-to-one
通過 many-to-one 元素,可以定義一種常見的與另一個持久化類的多對一關聯,這個表的一個外鍵引用目標表的主鍵字段。

java代碼:
1. <many-to-one 
2. name="propertyName" (1) 
3. column="column_name" (2)  
4. class="ClassName" (3) 
5. cascade="cascade_style"(4) 
6. fetch="join|select" (5) 
7. update="true|false" (6) 
8. insert="true|false" (7) 
9. property-ref="propertyNameFromAssociatedClass"(8) 
10. access="field|property|ClassName"(9) 
11. unique="true|false" (10) 
12. not-null="true|false" (11) 
13. optimistic-lock="true|false“ (12) 
14. lazy="proxy|no-proxy|false" (13) 
15. not-found="ignore|exception“ (14) 
16. entity-name="EntityName" (15) 
17. formula="arbitrary SQL expression"(16) 
18. /> 
19.   
(1)name:屬性名。
(2)column(可選):外鍵字段的名稱。也可以通過嵌套的 <column> 指定。
(3)class(可選 — 默認是通過反射得到的屬性類型):被關聯的類的名字。
(4)cascade(級聯)(可選)表明操作是否從父對象級聯到被關聯的對象。
(5)fetch(可選 — 默認為 select):在外連接抓取和序列選擇抓取兩者中選擇其一。
(6)update, insert(可選 — 默認為 true)指定對應的字段是否包含在用於 UPDATE 和/或 INSERT的 SQL 語句中。如果二者都是false,則這是一個純粹的 “外源性(derived)”關聯,它的值是通過
 映射到同一個(或多個)字段的某些其他屬性得到 或者通過 trigger(觸發器)、或其他程序生成。
(7)property-ref:(可選)被關聯到此外鍵的類中的對應屬性的名字。如果不指定,使用被關聯類的主鍵
(8)access(可選 — 默認為 property):Hibernate 用來訪問屬性值的策略。
(9)unique(可選):使用 DDL 為外鍵字段生成一個唯一約束。此外, 這也可以用作 propertyref的目標屬性。這使關聯同時具有一對一的效果。
(10)not-null(可選):使用 DDL 為外鍵字段生成一個非空約束。
(11)optimistic-lock(可選 — 默認為 true):指定這個屬性在做更新時是否需要獲得樂觀鎖定。換句話說,它決定這個屬性發生臟數據時版本(version)的值是否增長。
(12)lazy(可選 — 默認為 proxy):默認情況下,單點關聯是經過代理的。lazy="no-proxy" 指定此屬性應該在實例變量第一次被訪問時應該延遲抓取(fetche lazily)(需要運行時字節碼的增強)。lazy="false" 指定此關聯總是被預先抓取。
(13)not-found(可選 – 默認為exception):指定如何處理引用缺失行的外鍵:ignore 會把缺失的行作為一個空關聯處理。
(14)entity-name(可選):被關聯的類的實體名。
(15)formula(可選):SQL 表達式,用於定義 computed(計算出的)外鍵值。
 
one-to-many
通過 one-to-many 元素,可以定義一種常見的與另一個持久化類的一對多關聯。

java代碼:
1. <one-to-many 
2. class="ClassName"(1) 
3. not-found="ignore|exception"(2) 
4. entity-name="EntityName"(3) 
5. /> 
(1)class(必需):被關聯類的名稱。
 
(2)not-found(可選 – 默認為exception):指明若緩存的標示值關聯的行缺失,該如何處理:ignore 會把缺失的行作為一個空關聯處理。
 
(3)entity-name(可選):被關聯的類的實體名,作為 class 的替代。
 
註意:<one-to-many> 元素不需要定義任何字段。也不需要指定表名。
還是用示例來看,下面作一個雙向一對多的關系示例:
1:新建一個Parent對象和Child對象,Parent對象裡面有一個Child對象的集合,Child對象裡面有一個對Parent對象的引用,如下:

java代碼:
1. public class Parent{ 
2.   private String id; 
3.   private String name; 
4.   private Set children = new HashSet(); 
5. …… 
6. } 
7. public class Child { 
8.   private String uuid; 
9.   private String address; 
10.   private String postCode; 
11.   private Parent parent; 
12. …… 
13. } 
14. Parent.hbm.xml: 
15. <?xml version="1.0" encoding="UTF-8"?> 
16. <!DOCTYPE hibernate-mapping PUBLIC 
17.     "-//Hibernate/Hibernate Mapping DTD 3.0//EN" 
18.     "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> 
19. <hibernate-mapping> 
20.     <class name="cn.javass.h3.parentchild.Parent" table="tbl_parent" dynamic-update="true" dynamic-insert="true" lazy="true"> 
21.         <id name="id" column="uuid" type="java.lang.String" unsaved-value="any"> 
22.             <generator class="assigned"> </generator> 
23.         </id> 
24.         <property name="name"  update="true" insert="true"/>        
25. <set name="children" inverse="true"  cascade="all"> 
26. <key column="tbl_parent_fk"/> 
27. <one-to-many class="cn.javass.h3.parentchild.Child"/> 
28. </set> 
29.     </class> 
30. </hibernate-mapping> 
31.   
32. Child.hbm.xml: 
33. <?xml version="1.0" encoding="UTF-8"?> 
34. <!DOCTYPE hibernate-mapping PUBLIC 
35.     "-//Hibernate/Hibernate Mapping DTD 3.0//EN" 
36.     "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> 
37. <hibernate-mapping> 
38.     <class name="cn.javass.h3.parentchild.Child" table="tbl_child" dynamic-update="true" dynamic-insert="true" lazy="true"> 
39.         <id name="uuid" column="uuid" type="java.lang.String" unsaved-value="any"> 
40.             <generator class="assigned"> </generator> 
41.         </id> 
42.         <property name="address"  update="true" insert="true"/> 
43. <property name="postCode"  update="true" insert="true"/> 
44. <many-to-one 
45. name="parent" 
46. column="tbl_Parent_fk" 
47. class="cn.javass.h3.parentchild.Parent" 
48. not-null="true" 
49. /> 
50.     </class> 
51. </hibernate-mapping> 
客戶端測試TestMR.java文件太長,直接看演示好瞭。
在這個測試裡面分別演示瞭單獨的操作和級聯的操作。
inverse:指定由哪一方來維護雙方的關聯關系,默認是false,也就是雙方都維護,主要用在一對多 和 多對多中。
在一對多中使用inverse的時候,通常是在”一”這一端設置inverse=true,他的意思就是由多的那一端去維護關系,非反向端用於把內存中的表示保存到數據庫中。如下:
Parent p = new Parent();
Child c = new Child();
c.setParent(p); //維護父子之間關系
p.getChildren().add(c);
ninverse還有一個重要特點就是會優化Sql
ncascade:定義對象的級聯關系
all : 所有情況下均進行關聯操作
none:所有情況下均不進行關聯操作。這是默認值
save-update:在執行save/update/saveOrUpdate時進行關聯操作
delete:在執行delete時進行關聯操作
簡單的示范一下雙向的一對一
雙向一對一的操作是類似於雙向一對多的,隻是配置上有一些不同:
1:Parent裡面的配置,示例如下:
<one-to-one name= “cc” class= “cn.javass.h3.parentchild.Child” cascade= “all”  property-ref= “parent“/>這裡的property-ref參照的是Child對象裡面的屬性。
2:Child裡面的配置,不是使用one-to-one,而是仍使用many-to-one,示例如下:
<many-to-one name=“parent” class=“cn.javass.h3.parentchild.Parent” column=“tbl_parent_fk”/>
3:測試程序裡面,原來調用集合的地方,變成調用一個Child對象,其他就差不多瞭,可以測試看看。
 
Hibernate4的過濾器
Hibernate3 新增瞭對某個類或者集合使用預先定義的過濾器條件(filter criteria)的功能。過濾器條件相當於定義一個 非常類似於類和各種集合上的“where”屬性的約束子句, 但是過濾器條件可以帶參數,應用程序可以在運行時決 定是否啟用給定的過濾器,以及使用什麼樣的參數值。過濾器的用法很像數據庫視圖,隻不過是在應用程序中確定使用什麼樣的參數的。
 
定義過濾器
要使用過濾器,首先要在相應的映射節點中定義。而定義一個過濾器,要用到位於 <hibernatemapping/>節點之內的 <filter-def/> 節點:
示例如下:

java代碼:
1. <filter-def name="myFilter"> 
2. <filter-param name="myFilterParam" type="string"/> 
3. </filter-def> 
 
使用過濾器之配置
定義好之後,就可以在某個類中使用這個過濾器:

java代碼:
1. <class name="myClass" …> 
2. … 
3. <filter name="myFilter" condition=":myFilterParam=FILTERED_COLUMN"/> 
4. </class> 
或者也可以在某個集合使用它:

java代碼:
1. <set …> 
2. <filter name="myFilter" condition=":myFilterParam=FILTERED_COLUMN"/> 
3. </set> 
 
在同時可以使用多個過濾器。
使用過濾器之程序
在程序中,需要使用session接口中的:enableFilter(String filterName),getEnabledFilter(String filterName),和 disableFilter(String filterName)方法。Session中默認不啟用過濾器,必須通過enabledFilter() 方法顯式的啟用。
示例代碼session.enableFilter("myFilter").setParameter("myFilterParam", "some-value");
過濾器示例
在Parent.hbm.xml中定義有如下的過濾器:

java代碼:
1. <filter-def name="myFilter"> 
2. <filter-param name="myFilterParam" type="string"/> 
3. </filter-def> 
4. <filter-def name="myFilter2"> 
5. <filter-param name="myFilterParam" type="int"/> 
6. <filter-param name="myFilterParam2" type="int"/> 
7. </filter-def> 
在定義Child集合中使用

java代碼:
1. <set name="children" inverse="true" cascade="all" fetch="select" lazy="false" batch-size="3"> 
2. <key column="tbl_parent_fk"/> 
3. <one-to-many class="cn.javass.h3.parentchild.Child" /> 
4. <filter name="myFilter" 
5. condition="address like :myFilterParam"></filter> 
6. <filter name="myFilter2" condition="uuid &gt;= :myFilterParam and uuid &lt;= :myFilterParam2"></filter> 
7. </set> 
程序中使用示例

java代碼:
1. s.enableFilter("myFilter").setParameter("myFilterParam", "%1%"); 
2. s.enableFilter("myFilter2").setParameter("myFilterParam", 1) 
3. .setParameter("myFilterParam2", 3); 
4. Query q = s.createQuery("select p from Parent as p  "); 
5. System.out.println("p==="+p.getChildren()); 
6.   

 作者 :jinnianshilongnian

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。