hibernate 級聯(cascade和inverse)

 級聯(Cascade) : 二個以上的設備經過某種方式鏈接起來,能起到擴容的效果就是級聯。Hibernate級聯(Cascade)是用來講明數據庫中兩個表之間相互關係(一對一,一對多,多對多)中,當對主對象進行某種操做時,是否對其關聯的從對象也做相似的操做(好比有對象Department和Employee,它們之間是一對多的關係,當保存Department時,其對應的Employee是否也相應的保存),常見的級聯(Cascade)有: mysql

(1)none:在保存,刪除或修改當前對象時,不對其附屬對象(關聯對象)進行級聯操做。它是默認值。
(2)save-update:在保存,更新當前對象時,級聯保存,更新附屬對象(臨時對象,遊離對象)。
(3)delete:在刪除當前對象時,級聯刪除附屬對象。
(4)all:全部狀況下均進行級聯操做,即包含save-update和delete等等操做。
(5)delete-orphan:刪除此對象的同時刪除與當前對象解除關係的孤兒對象(僅僅使用於一對多關聯關係中)。 sql

對Hibernate session的每個基本操做,如:persist(),merge(),saveOrUpdate(),delete(),lock(),refresh(),evict(),replicate(),都有一個相關的級聯形式與之對應,他們分別命名爲:create,merge,save-update,delete,lock,refresh,evict,replicate.若是你想對某種關聯操做設置級聯,你只要在映射文件中配置便可,如: 數據庫

Xml代碼   收藏代碼
  1. <one-to-one name="person" cascade="persist"/>  

 若是想同時給級聯設置多個值,能夠如此設置: session

Xml代碼   收藏代碼
  1. <one-to-one name="person" cascade="persist,delete,lock"/>  

建議: app

1. 通常對<many-to-one>或<many-to-many>關聯關係不設置級聯(Cascade)操做,級聯(Cascade)操做經常被用於<one-to-one>和<one-to-many>. dom

2. 若是子對象的生命週期與父對象的生命週期是聯繫在一塊兒的(一致的),能夠經過設置級聯cascade="all,delete-orphan"來指定它的對象生命週期。 ide

 

cascade測試程序以下: 性能

1。實體類: 測試

Department以下: this

Java代碼   收藏代碼
  1. package com.reiyen.hibernate.domain;  
  2. public class Department {  
  3.   
  4.     private int id;  
  5.     private String name;  
  6.     private Set<Employee> emps;  
  7.       //setter和getter方法  
  8. }  

 Employee以下:

Java代碼   收藏代碼
  1. package com.reiyen.hibernate.domain;  
  2.   
  3. public class Employee {  
  4.   
  5.     private int id;  
  6.     private String name;  
  7.     private Department department;  
  8.        //setter和getter方法  
  9.      @Override  
  10.     public String toString() {  
  11.         return "id=" + this.id + " name=" + this.name;  
  12.     }  
  13. }  

 映射文件以下:

Department.hbm.xml映射文件以下:

Xml代碼   收藏代碼
  1. <?xml version="1.0"?>  
  2. <!DOCTYPE hibernate-mapping PUBLIC   
  3.     "-//Hibernate/Hibernate Mapping DTD 3.0//EN"  
  4.     "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">  
  5. <hibernate-mapping package="com.reiyen.hibernate.domain">  
  6.     <class name="Department">  
  7.         <id name="id">  
  8.             <generator class="native" />  
  9.         </id>  
  10.         <property name="name" />  
  11. //配置多個cascade值,只需用,分隔便可  
  12.             <set name="emps" cascade="save-update,delete">  
  13.             <key column="depart_id" />  
  14.             <one-to-many class="Employee"/>  
  15.             </set>  
  16.     </class>  
  17. </hibernate-mapping>  

 Employee.hbm.xml映射文件以下:

Xml代碼   收藏代碼
  1. <?xml version="1.0"?>  
  2. <!DOCTYPE hibernate-mapping PUBLIC   
  3.     "-//Hibernate/Hibernate Mapping DTD 3.0//EN"  
  4.     "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">  
  5. <hibernate-mapping package="com.reiyen.hibernate.domain">  
  6.     <class name="Employee">  
  7.         <id name="id">  
  8.             <generator class="native" />  
  9.         </id>  
  10.         <property name="name" unique="true"/>  
  11.         <!-- name="department" 這個名稱必須與Employee中的屬性名一致. 設置了column="depart_id",默認它會去department中找id與depart_id值相等的對象.若是要找name的值與depart_id相等的對象,則能夠設置property-ref="name" -->  
  12.         <many-to-one name="department" column="depart_id" />  
  13.     </class>  
  14. </hibernate-mapping>  
 

 測試程序以下:

Java代碼   收藏代碼
  1. public class One2Many {  
  2.   
  3.     public static void main(String[] args) {  
  4.         Department depart = add();  
  5.         Department department = queryDepart(depart.getId());  
  6.     }  
  7.   
  8.     static Department queryDepart(int departId) {  
  9.         Session s = null;  
  10.         Transaction tx = null;  
  11.         try {  
  12.             s = HibernateUtil.getSession();  
  13.             tx = s.beginTransaction();  
  14.             Department depart = (Department) s.get(Department.class, departId);  
  15.             System.out.println("emps:" + depart.getEmps());  
  16.             tx.commit();  
  17.             return depart;  
  18.         } finally {  
  19.             if (s != null)  
  20.                 s.close();  
  21.         }  
  22.     }  
  23.   
  24.     static Department add() {  
  25.         Session s = null;  
  26.         Transaction tx = null;  
  27.         try {  
  28.             Department depart = new Department();  
  29.             depart.setName("department name");  
  30.               
  31.             Employee employee1 = new Employee();  
  32.             employee1.setName("employee1 name1");  
  33.               
  34.             Employee employee2 = new Employee();  
  35.             employee2.setName("employee2 name2");  
  36.               
  37.             Set<Employee> set = new HashSet<Employee>();  
  38.             set.add(employee1);  
  39.             set.add(employee2);  
  40.             depart.setEmps(set);  
  41.               
  42.             s = HibernateUtil.getSession();  
  43.             tx = s.beginTransaction();  
  44.             s.save(depart);  
  45.             //s.save(employee1); //1  
  46.             //s.save(employee2); //2  
  47.             tx.commit();  
  48.             return depart;  
  49.         } finally {  
  50.             if (s != null)  
  51.                 s.close();  
  52.         }  
  53.     }  
  54. }  

 由於在映射文件Department.hbm.xml文件中給集合映射set配置了級聯(cascade="save-update"),因此將測試程序中註釋爲1,2的兩句程序(保存set集合中Employee元素的程序)註釋掉,程序也同樣能將這兩個Employee元素保存進數據庫中,測試結果以下所示:

控制檯打印信息以下:

Hibernate: insert into Department (name) values (?)
Hibernate: insert into Employee (name, depart_id) values (?, ?)
Hibernate: insert into Employee (name, depart_id) values (?, ?)
Hibernate: update Employee set depart_id=? where id=?
Hibernate: update Employee set depart_id=? where id=?

數據庫表中記錄以下:

mysql> select * from department;
+----+-----------------+
| id | name            |
+----+-----------------+
|  1 | department name |
+----+-----------------+
1 row in set (0.00 sec)

mysql> select * from employee;
+----+-----------------+-----------+
| id | name            | depart_id |
+----+-----------------+-----------+
|  1 | employee1 name1 |         1 |
|  2 | employee2 name2 |         1 |
+----+-----------------+-----------+
2 rows in set (0.00 sec)

 

若是Department映射文件中的set集合映射沒有設置級聯,一樣的程序進行測試,則會拋出以下異常,控制檯打印信息以下:

Hibernate: insert into Department (name) values (?)
Hibernate: update Employee set depart_id=? where id=?

org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: com.reiyen.hibernate.domain.Employee

當把Department實例持久化進數據庫後,更新它相關聯的Employee對象時拋出異常,由於此時關聯的Employee是瞬時對象,而不是一個持久化對象。

 

inverse: 表示「是否放棄維護關聯關係」(在Java裏兩個對象產生關聯時,對數據庫中表的影響),hibernate的缺省值爲false。在<one-to-many>和<many-to-many>關聯關係的集合定義中使用,inverse="true"表示該對象不維護關聯關係。該屬性的值在使用有序集合時(list,array等)時通常設置爲false.<one-to-many>維護關聯關係就是更新外鍵;<many-to-many>維護關聯關係就是在中間表中增減記錄!

cascade 和inverse有什麼區別?   能夠這樣理解,cascade 定義的是關係兩端對象到對象的級聯關係;而inverse定義的是關係和對象的級聯關係。 

inverse測試程序以下:

測試類以下:

Java代碼   收藏代碼
  1. public class One2Many {  
  2.   
  3.     public static void main(String[] args) {  
  4.         Department depart = add();  
  5.         Department department = queryDepart(depart.getId());  
  6.     }  
  7.   
  8.     static Department queryDepart(int departId) {  
  9.         Session s = null;  
  10.         Transaction tx = null;  
  11.         try {  
  12.             s = HibernateUtil.getSession();  
  13.             tx = s.beginTransaction();  
  14.             Department depart = (Department) s.get(Department.class, departId);  
  15.             System.out.println("emps:" + depart.getEmps());  
  16.             tx.commit();  
  17.             return depart;  
  18.         } finally {  
  19.             if (s != null)  
  20.                 s.close();  
  21.         }  
  22.     }  
  23.   
  24.     static Department add() {  
  25.         Session s = null;  
  26.         Transaction tx = null;  
  27.         try {  
  28.             Department depart = new Department();  
  29.             depart.setName("department name");  
  30.               
  31.             Employee employee1 = new Employee();  
  32.             employee1.setDepartment(depart); //1 對象模型:創建兩個對象的關聯   
  33.             employee1.setName("employee1 name1");  
  34.               
  35.             Employee employee2 = new Employee();  
  36.             employee2.setDepartment(depart); //2 對象模型:創建兩個對象的關聯   
  37.             employee2.setName("employee2 name2");  
  38.               
  39.             Set<Employee> set = new HashSet<Employee>();  
  40.             set.add(employee1);  
  41.             set.add(employee2);  
  42.             depart.setEmps(set); //3 對象模型:創建兩個對象的關聯   
  43.               
  44.             s = HibernateUtil.getSession();  
  45.             tx = s.beginTransaction();  
  46.             //s.save(depart); //4  
  47.             s.save(employee1);  
  48.             s.save(employee2);  
  49.             s.save(depart); //5  
  50.             tx.commit();  
  51.             return depart;  
  52.         } finally {  
  53.             if (s != null)  
  54.                 s.close();  
  55.         }  
  56.     }  
  57. }  

 先在Department.hbm.xml文件中不設置inverse進行測試:

1。測試結果以下,控制檯打印了四條update語句(這是由於關聯對象雙方都要去維護這種關聯關係所致使的結果):

Hibernate: insert into Employee (name, depart_id) values (?, ?)
Hibernate: insert into Employee (name, depart_id) values (?, ?)
Hibernate: insert into Department (name) values (?)
Hibernate: update Employee set name=?, depart_id=? where id=?
Hibernate: update Employee set name=?, depart_id=? where id=?
Hibernate: update Employee set depart_id=? where id=?
Hibernate: update Employee set depart_id=? where id=?

2。若是將測試程序中的標記爲1,2的語句註釋掉,或將標記爲3的語句註釋掉再進行測試,控制檯打印的結果就會少兩條update語句,以下所示(此時程序的功能與1一致,數據庫中記錄也同樣,只是此時在對象模型上不是完整的而已,由於在對象模型上,只是告訴了一方與另外一方的關係,而反過爲,另外一方則不知他對應的對象了。其實這對程序沒有任何影響):

Hibernate: insert into Employee (name, depart_id) values (?, ?)
Hibernate: insert into Employee (name, depart_id) values (?, ?)
Hibernate: insert into Department (name) values (?)
Hibernate: update Employee set name=?, depart_id=? where id=?
Hibernate: update Employee set name=?, depart_id=? where id=?

3。 或者將標記爲5的語句註釋掉,同時將標記爲4的語句的註釋去掉,再運行;結果與2。的同樣只打印兩條update語句。但這兩條update語句與2中的不一樣,由於這兩條語句是因爲

Java代碼   收藏代碼
  1. depart.setEmps(set); //3 對象模型:創建兩個對象的關聯   

而產生的。此時爲何只有這兩條update語句了呢?由於先保存了Department對象,因此再保存Employee時,直接就能夠將Department對象的值取到,直接保存在數據庫中了,而再也不須要update.因此就少了如下的兩條SQL語句:

Hibernate: update Employee set name=?, depart_id=? where id=?
Hibernate: update Employee set name=?, depart_id=? where id=?

.產生的SQL語句以下所示:

Hibernate: insert into Department (name) values (?)
Hibernate: insert into Employee (name, depart_id) values (?, ?)
Hibernate: insert into Employee (name, depart_id) values (?, ?)
Hibernate: update Employee set depart_id=? where id=?
Hibernate: update Employee set depart_id=? where id=?

4。在cascade程序的基礎上進行修改。將Department.hbm.xml文件進行修改,將cascade="save-update,delete"替換成inverse="true"便可,其他的地方保持原樣。再繼續運行測試程序,這是控制檯打印的信息與測試2的結果一致,也只有兩打update語句,但此時在對象模型上也是完整的,由於對象模型知道了相互對應的另外一方,此時Department對象已經放棄關聯關係的維護了,只有Employee對象去維護他們之間的關聯關係,因此只產生了兩條update語句。inverse"是否放棄維護關聯關係"通常在one的一方設置(其實hibernate不容許多的一端放棄維護關聯關係,而由one的一端維護,由於這是很是低效率的),這比比如一個Teacher與Student這種現實生活中的關係,你讓老師記住每一個學生的名字這是比較困難的一件事情,因此你就讓老師不用去記學生,而讓學生去記老師就好了,這會簡單的多,而只要每一個學生記住了老師,這樣他們的關聯也就能夠維持了。同時,這樣還能夠提升效率。在上例中,當設置了級聯關係後,若是先保存Department,再保存Employee(也就是再執行上例中第三種操做),控制檯就不會再產生update語句了。以下所示:

Hibernate: insert into Department (name) values (?)
Hibernate: insert into Employee (name, depart_id) values (?, ?)
Hibernate: insert into Employee (name, depart_id) values (?, ?)

但此時有一點須要很是注意:由於你已經設置了讓Department對象放棄維護關聯關係了,因此此時,測試程序中標記爲1,2的語句必定不能少,不然在數據庫中就體現不了它們之間的關聯關係了。註釋掉標記爲1,2的兩句程序,從新運行,結果以下所示,控制檯打印信息以下:

Hibernate: insert into Employee (name, depart_id) values (?, ?)
Hibernate: insert into Employee (name, depart_id) values (?, ?)
Hibernate: insert into Department (name) values (?)

沒有了更新外鍵的update語句了。同時查看數據庫表中記錄,employee表中depart_id的值爲null了,以下所示:

mysql> select * from department;
+----+-----------------+
| id | name            |
+----+-----------------+
|  1 | department name |
+----+-----------------+
1 row in set (0.00 sec)

mysql> select * from employee;
+----+-----------------+-----------+
| id | name            | depart_id |
+----+-----------------+-----------+
|  1 | employee1 name1 |      NULL |
|  2 | employee2 name2 |      NULL | 
+----+-----------------+-----------+
2 rows in set (0.00 sec)

但能夠將測試程序中標記爲3的語句(也即創建Department與Employee之間關係的語句)註釋掉,再從新運行,結果不會有什麼影響,由於如今Department已再也不維護關聯關係了,因此這句程序要與不要沒有區別。

5。若是將上面程序中的set集合映射改爲list集合映射來進行(set集合改爲list集合映射的細節請參看我寫的hibernate集合類(Collections)映射 ,除了修改映射文件外,還得修改測試程序),在list集合映射中設置級聯,以下所示:

Xml代碼   收藏代碼
  1. <list name="emps" inverse="true">  
  2.             <key column="depart_id" />  
  3.             <!-- list-index:用來記錄加入list集合的元素的順序 ,會必定程度影響性能,因此可使用bag替代list-->  
  4.             <list-index column="order_col" />  
  5.             <one-to-many class="Employee" />  
  6.         </list>  

 再運行如上的測試程序,則會出現以下的異常:

org.hibernate.HibernateException: null index column for collection: com.reiyen.hibernate.domain.Department.emps

由於在list集合映射中須要維護加入list集合中的元素的順序,而inverse=「true」是放棄維護關聯關係。當對象之間的關聯關係都不維護,再談添加入的順序,已沒有什麼意義。因此及在使用hibernate的有序集合時,通常將inverse設置成false.

相關文章
相關標籤/搜索