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 }
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 }
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>
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>
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 }
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 }
這樣操做顯然不行,致使出現異常:瞬時對象異常,一個持久太對象關聯了一個瞬時態對象,那就說明咱們不能只保存一方。那若是咱們就想只保存一個方向應該如何進行操做呢?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>
值得注意的是,不管是哪一方維護數據,都必需要與另外一方創建關係,才能自動保存和更新:
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 }
默認狀況下若是客戶下面還有聯繫人,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. 級聯屬性