3.4 Hibernate多表操做

 

 

1. 一對多、多對一java

Hibernate框架實現了ORM的思想,將關係數據庫中表的數據映射成對象,使開發人員把對數據庫的操做轉化爲對對象的操做,Hibernate的關聯關係映射主要包括多表的映射配置、數據的增長、刪除等。數據庫

數據庫中多表之間存在着三種關係,也就是系統設計中的三種實體關係。如圖所示。session

從圖能夠看出,系統設計的三種實體關係分別爲:多對多、一對多和一對一關係。在數據庫中,實體表之間的關係映射是採用外鍵來描述的,具體以下:app

  1. 1對多  建表原則:在多的一方建立外鍵指向一的一方的主鍵:框架

 1 package com.eagle.domain;
 2 
 3 import java.util.HashSet;
 4 import java.util.Set;
 5 
 6 public class Customer {
 7 
 8     private Long cust_id;
 9     private String cust_name;
10     private String cust_source;
11     private String cust_industry;
12     private String cust_level;
13     private String cust_phone;
14     private String cust_mobile;
15     private Set<LinkMan> linkmans = new HashSet<LinkMan>();        // 一個客戶有多個聯繫人,客戶中應該放有聯繫人的集合
16 
17     public Long getCust_id() {
18         return cust_id;
19     }
20 
21     public void setCust_id(Long cust_id) {
22         this.cust_id = cust_id;
23     }
24 
25     public String getCust_name() {
26         return cust_name;
27     }
28 
29     public void setCust_name(String cust_name) {
30         this.cust_name = cust_name;
31     }
32 
33     public String getCust_source() {
34         return cust_source;
35     }
36 
37     public void setCust_source(String cust_source) {
38         this.cust_source = cust_source;
39     }
40 
41     public String getCust_industry() {
42         return cust_industry;
43     }
44 
45     public void setCust_industry(String cust_industry) {
46         this.cust_industry = cust_industry;
47     }
48 
49     public String getCust_level() {
50         return cust_level;
51     }
52 
53     public void setCust_level(String cust_level) {
54         this.cust_level = cust_level;
55     }
56 
57     public String getCust_phone() {
58         return cust_phone;
59     }
60 
61     public void setCust_phone(String cust_phone) {
62         this.cust_phone = cust_phone;
63     }
64 
65     public String getCust_mobile() {
66         return cust_mobile;
67     }
68 
69     public void setCust_mobile(String cust_mobile) {
70         this.cust_mobile = cust_mobile;
71     }
72     
73     public Set<LinkMan> getLinkmans() {
74         return linkmans;
75     }
76 
77     public void setLinkmans(Set<LinkMan> linkmans) {
78         this.linkmans = linkmans;
79     }
80 
81     @Override
82     public String toString() {
83         return "Customer [cust_id=" + cust_id + ", cust_name=" + cust_name + ", cust_source=" + cust_source
84                 + ", cust_industry=" + cust_industry + ", cust_level=" + cust_level + ", cust_phone=" + cust_phone
85                 + ", cust_mobile=" + cust_mobile + "]";
86     }
87 
88 }
Customer
 1 package com.eagle.domain;
 2 
 3 public class LinkMan {
 4 
 5     private Long lkm_id;
 6     private String lkm_name;
 7     private String lkm_gender;
 8     private String lkm_phone;
 9     private String lkm_mobile;
10     private String lkm_email;
11     private String lkm_qq;
12     private String lkm_position;
13     private String lkm_memo;
14     private Customer customer;     // Hibernate是一個ORM的框架:在關係型的數據庫中描述表與表之間的關係,使用的是外鍵,開發語言使用是Java,面向對象的。
15     public Long getLkm_id() {
16         return lkm_id;
17     }
18     public void setLkm_id(Long lkm_id) {
19         this.lkm_id = lkm_id;
20     }
21     public String getLkm_name() {
22         return lkm_name;
23     }
24     public void setLkm_name(String lkm_name) {
25         this.lkm_name = lkm_name;
26     }
27     public String getLkm_gender() {
28         return lkm_gender;
29     }
30     public void setLkm_gender(String lkm_gender) {
31         this.lkm_gender = lkm_gender;
32     }
33     public String getLkm_phone() {
34         return lkm_phone;
35     }
36     public void setLkm_phone(String lkm_phone) {
37         this.lkm_phone = lkm_phone;
38     }
39     public String getLkm_mobile() {
40         return lkm_mobile;
41     }
42     public void setLkm_mobile(String lkm_mobile) {
43         this.lkm_mobile = lkm_mobile;
44     }
45     public String getLkm_email() {
46         return lkm_email;
47     }
48     public void setLkm_email(String lkm_email) {
49         this.lkm_email = lkm_email;
50     }
51     public String getLkm_qq() {
52         return lkm_qq;
53     }
54     public void setLkm_qq(String lkm_qq) {
55         this.lkm_qq = lkm_qq;
56     }
57     public String getLkm_position() {
58         return lkm_position;
59     }
60     public void setLkm_position(String lkm_position) {
61         this.lkm_position = lkm_position;
62     }
63     public String getLkm_memo() {
64         return lkm_memo;
65     }
66     public void setLkm_memo(String lkm_memo) {
67         this.lkm_memo = lkm_memo;
68     }
69     public Customer getCustomer() {
70         return customer;
71     }
72     public void setCustomer(Customer customer) {
73         this.customer = customer;
74     }
75     @Override
76     public String toString() {
77         return "LinkMan [lkm_id=" + lkm_id + ", lkm_name=" + lkm_name + ", lkm_gender=" + lkm_gender + ", lkm_phone="
78                 + lkm_phone + ", lkm_mobile=" + lkm_mobile + ", lkm_email=" + lkm_email + ", lkm_qq=" + lkm_qq
79                 + ", lkm_position=" + lkm_position + ", lkm_memo=" + lkm_memo + ", customer=" + customer + "]";
80     }
81     
82 }
LinkMan
 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
 3 <hibernate-mapping>
 4     <class name="com.eagle.domain.Customer" table="cst_customer">
 5         <id name="cust_id" column="cust_id" >
 6             <generator class="native"></generator>
 7         </id>    
 8         <property name="cust_name" column="cust_name"></property>
 9         <property name="cust_source" column="cust_source"></property>
10         <property name="cust_industry" column="cust_industry"></property>
11         <property name="cust_level" column="cust_level"></property>
12         <property name="cust_phone" column="cust_phone"></property>
13         <property name="cust_mobile" column="cust_mobile"></property>
14         
15         <!-- 集合,一對多關係,在配置文件中配置 -->
16         <!--
17             name屬性:    集合屬性名
18             column屬性:    外鍵列名
19             class屬性:    與我關聯的對象完整類名
20         -->
21         <set name="linkmans">
22             <key column="lkm_cust_id"></key>
23             <one-to-many class="com.eagle.domain.LinkMan" />
24         </set>
25     </class>
26 </hibernate-mapping>
Customer.hbm.xml
 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
 3 <hibernate-mapping>
 4     <class name="com.eagle.domain.LinkMan" table="cst_linkman">
 5         <id name="lkm_id" column="lkm_id">
 6             <generator class="native"></generator>
 7         </id>
 8         <property name="lkm_name"></property>
 9         <property name="lkm_gender"></property>
10         <property name="lkm_phone"></property>
11         <property name="lkm_mobile"></property>
12         <property name="lkm_email"></property>
13         <property name="lkm_qq"></property>
14         <property name="lkm_position"></property>
15         <property name="lkm_memo"></property>
16         
17         <!-- 
18             name屬性:    引用屬性名
19             column:        外鍵列名
20             class屬性:    與我關聯的對象完整類名
21          -->
22         <many-to-one name="customer" column="lkm_cust_id" class="com.eagle.domain.Customer">
23         </many-to-one>
24     </class>
25 </hibernate-mapping>
LinkMan.hbm.xml
 1 package com.eagle.test;
 2 
 3 
 4 import org.hibernate.Session;
 5 import org.hibernate.Transaction;
 6 import org.junit.Test;
 7 
 8 import com.eagle.domain.Customer;
 9 import com.eagle.domain.LinkMan;
10 import com.eagle.utils.HibernateUtils;
11 
12 public class HibernateTest {
13 
14     private Session session = HibernateUtils.getCurrentSession();
15     
16     @Test
17     public void test() {
18         Transaction tx = session.beginTransaction();
19         // 建立一個客戶
20         Customer customer = new Customer();
21         customer.setCust_name("百度");
22         // 建立3個聯繫人
23         LinkMan linkMan1 = new LinkMan();
24         linkMan1.setLkm_name("姜總");
25         LinkMan linkMan2 = new LinkMan();
26         linkMan2.setLkm_name("李祕書");
27         LinkMan linkMan3 = new LinkMan();
28         linkMan3.setLkm_name("王助理");
29         customer.getLinkmans().add(linkMan1);
30         customer.getLinkmans().add(linkMan2);
31         customer.getLinkmans().add(linkMan3);
32         
33         session.save(customer);
34         session.save(linkMan1);
35         session.save(linkMan2);
36         session.save(linkMan3);
37         tx.commit();
38     }
39 
40 }
test

 

  1. 使用set集合來描述Customer.java類中的屬性linkMans。在Hibernate的映射文件中,使用<set>標籤用來描述被映射類中的Set集合,<key>標籤的column屬性值對應文件多的一方的外鍵名稱,在Customer.java客戶類中,客戶與聯繫人是一對多的關係,Hibernate的映射文件中,使用<one-to-many>標籤來描述持久化類的一對多關聯,其中class屬性用來描述映射的關聯類。dom

  2. <many-to-one>標籤訂義兩個持久化類的關聯,這種關聯是數據表間的多對一關聯,聯繫人與客戶就是多對一的關係,因此用<many-to-one>標籤來描述。<many-to-one>標籤的name屬性用來描述customer在LinkMan.java類中的屬性的名稱,class屬性用來指定映射的類,column屬性值對應表中的外鍵列名。ide

   3. 這就是雙向關聯,那麼既然已經進行了雙向的關聯關係設置,那麼咱們還保存了雙方,那若是咱們只保存一方是否能夠呢?也就是說咱們創建了雙向的維護關係,只保存客戶或者只保存聯繫人是否能夠。測試一下:測試

 1 @Test
 2     public void test1() {
 3         Transaction tx = session.beginTransaction();
 4         // 建立一個客戶
 5         Customer customer = new Customer();
 6         customer.setCust_name("百度");
 7         // 建立3個聯繫人
 8         LinkMan linkMan1 = new LinkMan();
 9         linkMan1.setLkm_name("姜總");
10         LinkMan linkMan2 = new LinkMan();
11         linkMan2.setLkm_name("李祕書");
12         LinkMan linkMan3 = new LinkMan();
13         linkMan3.setLkm_name("王助理");
14         customer.getLinkmans().add(linkMan1);
15         customer.getLinkmans().add(linkMan2);
16         customer.getLinkmans().add(linkMan3);
17         
18         session.save(customer);
19 //        session.save(linkMan1);
20 //        session.save(linkMan2);
21 //        session.save(linkMan3);
22         tx.commit();
23     }
test1

這樣操做顯然不行,致使出現異常:瞬時對象異常,一個持久太對象關聯了一個瞬時態對象,那就說明咱們不能只保存一方。那若是咱們就想只保存一個方向應該如何進行操做呢?this

咱們能夠使用Hibernate的級聯操做。spa

 

2. Hibernate的級聯操做

它的目的就是爲了簡化操做,少寫兩行代碼.

必定要用,save-update,不建議使用delete.


2.1 級聯保存或更新,save-update

級聯操做是指當主控方執行保存、更新或者刪除操做時,其關聯對象(被控方)也執行相同的操做。

在映射文件中經過對cascade屬性的設置來控制是否對關聯對象採用級聯操做,級聯操做對各類關聯關係都是有效的。

級聯是由方向性的,所謂的方向性指的是,在保存一的一方級聯多的一方和在保存多的一方級聯一的一方。

首先要肯定咱們要保存的主控方是哪一方,咱們要保存客戶,因此客戶是主控方,那麼須要在客戶的映射文件中進行以下配置:

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
 3 <hibernate-mapping>
 4     <class name="com.eagle.domain.LinkMan" table="cst_linkman">
 5         <id name="lkm_id" column="lkm_id">
 6             <generator class="native"></generator>
 7         </id>
 8         <property name="lkm_name"></property>
 9         <property name="lkm_gender"></property>
10         <property name="lkm_phone"></property>
11         <property name="lkm_mobile"></property>
12         <property name="lkm_email"></property>
13         <property name="lkm_qq"></property>
14         <property name="lkm_position"></property>
15         <property name="lkm_memo"></property>
16         
17         <!-- 
18             name屬性:    引用屬性名
19             column:        外鍵列名
20             class屬性:    與我關聯的對象完整類名
21          -->
22         <many-to-one name="customer" column="lkm_cust_id" class="com.eagle.domain.Customer" cascade="save-update">
23         </many-to-one>
24     </class>
25 </hibernate-mapping>
LinkMan.hbm.xml

值得注意的是,不管是哪一方維護數據,都必需要與另外一方創建關係,才能自動保存和更新:

  1. 假如customer維護數據,就必須customer與linkMan創建關係,而後只須要保存customer就能夠了:

  

  customer.getLinkmans().add(linkMan1);

customer.getLinkmans().add(linkMan2);

customer.getLinkmans().add(linkMan3);

  session.save(customer);

  2. 反之若是linkMan維護數據,就必須與customer創建關係,而後只需保存linkMan就能夠了:

  linkMan1.setCustomer(customer);

  session.save(linkMan1);

 1 @Test
 2     public void test3() {
 3         Transaction tx = session.beginTransaction();
 4         // 建立一個客戶
 5         Customer customer = new Customer();
 6         customer.setCust_name("百度");
 7         // 建立3個聯繫人
 8         LinkMan linkMan1 = new LinkMan();
 9         linkMan1.setLkm_name("姜總");
10         LinkMan linkMan2 = new LinkMan();
11         linkMan2.setLkm_name("李祕書");
12         LinkMan linkMan3 = new LinkMan();
13         linkMan3.setLkm_name("王助理");        
14         // 創建關係
15         linkMan1.setCustomer(customer);
16         customer.getLinkmans().add(linkMan2);
17         customer.getLinkmans().add(linkMan3);        
18 //        session.save(linkMan1);
19 //        session.save(customer);
20         session.save(linkMan2);
21         tx.commit();
22     }

  若是單獨執行18行,會發現有4條insert語句:由於linkMan1關聯了客戶,客戶又關聯了linkMan2和linkMan3,因此當保存linkMan1的時候,linkMan1是能夠進入到數據庫的,它關聯的customer也是會進入到數據庫的,linkMan2和linkMan3也一樣會進入到數據庫,因此有4條insert語句;

 

 

  若是單獨執行19行,這時咱們保存的是customer對象,customer進入數據庫,與之關聯的linkMan2和linkMan3也一樣進入到數據庫,因此是三條insert語句;

 

  若是單獨執行20行,只會執行1條insert語句,即linkMan2進入到數據庫,可是linkMan2沒有customer與之創建關係,因此客戶不會保存到數據庫。

 

2.2 Hibernate級聯刪除----delete

級聯刪除也是有方向性的,刪除customer的同時級聯刪除linkMan,也能夠刪除聯繫人同時級聯刪除客戶(這種需求不多)。

原來JDBC中刪除客戶和聯繫人的時候,若是有外鍵關係是不能夠刪除的,可是如今咱們使用了Hibernate,其實Hibernate能夠實現這樣的功能,可是不會刪除customer同時刪除聯繫人,默認狀況下Hibernate會怎麼作?

1     @Test
2     public void test4() {
3         Transaction tx = session.beginTransaction();
4         Customer customer = session.get(Customer.class, 1l);
5         session.delete(customer);
6         tx.commit();
7     }
test4

默認狀況下若是客戶下面還有聯繫人,Hibernate會將聯繫人的外鍵置爲null,而後去刪除客戶。那麼其實有的時候咱們須要刪除客戶的時候,同時將客戶關聯的聯繫人一併刪除。這個時候咱們就須要使用Hibernate的級聯表保存操做了。

<set name="linkmans" cascade="delete">

<key column="lkm_cust_id"></key>

<one-to-many class="com.eagle.domain.LinkMan" />

</set>

 

 

測試代碼:

@Test

public void test4() {

Transaction tx = session.beginTransaction();

Customer customer = session.get(Customer.class, 1l);

session.delete(customer);

tx.commit();

}

 

一樣,咱們刪除的是聯繫人,那麼聯繫人是主控方,須要在聯繫人端配置:

<many-to-one name="customer" column="lkm_cust_id" class="com.eagle.domain.Customer" cascade="delete">

</many-to-one>

 測試代碼:

@Test

public void test5() {

Transaction tx = session.beginTransaction();

LinkMan linkMan1 = session.get(LinkMan.class, 3l);

session.delete(linkMan1);

tx.commit();

}

 

 

2.3 級聯all操做

cascade="delete" 至關於 save-update+delete

 

2.4 級聯雙向關聯產生多餘的SQL語句

@Test

public void test6() {

Transaction tx = session.beginTransaction();

LinkMan linkMan1 = session.get(LinkMan.class, 1l);

Customer customer = session.get(Customer.class, 2l);

linkMan1.setCustomer(customer);

customer.getLinkmans().add(linkMan1);

tx.commit();

}

 由於雙向維護關係,並且持久態對象能夠自動更新數據庫,更新customer的時候會修改一次外鍵,更新聯繫人的時候一樣也會修改一次外鍵。這樣會產生多餘的SQL:

解決辦法很簡單,只須要一方放棄外鍵維護權便可。

<set name="linkmans" cascade="all" inverse="true">

<key column="lkm_cust_id"></key>

<one-to-many class="com.eagle.domain.LinkMan" />

</set>

 

3. 多對多

關係表達:

  1. 在表中

 

  2. 對象中

 

  3. ORM原數據中

 

多表操做:

  1. 操做關聯屬性

  

  2. inverse屬性

 

  3. 級聯屬性

相關文章
相關標籤/搜索