1:Hibernate的一對多操作(重點)
一對多映射配置
第一步:創建兩個實體類:客戶和聯系人(例)以客戶為一,聯系人為多:

1 package com.yinfu.entity; 2 3 public class LinkMan { 4 5 private Integer lkm_id; 6 private String lkm_name; 7 private String lkm_gender; 8 private String lkm_phone; 9 public Integer getLkm_id() { 10 return lkm_id; 11 } 12 public void setLkm_id(Integer lkm_id) { 13 this.lkm_id = lkm_id; 14 } 15 public String getLkm_name() { 16 return lkm_name; 17 } 18 public void setLkm_name(String lkm_name) { 19 this.lkm_name = lkm_name; 20 } 21 public String getLkm_gender() { 22 return lkm_gender; 23 } 24 public void setLkm_gender(String lkm_gender) { 25 this.lkm_gender = lkm_gender; 26 } 27 public String getLkm_phone() { 28 return lkm_phone; 29 } 30 public void setLkm_phone(String lkm_phone) { 31 this.lkm_phone = lkm_phone; 32 } 33 }

1 package com.yinfu.entity; 2 3 public class Customer { 4 5 private Integer cid; 6 private String custName; 7 private String custLevel; 8 private String custSource; 9 private String custPhone; 10 private String custMobile; 11 public Integer getCid() { 12 return cid; 13 } 14 public void setCid(Integer cid) { 15 this.cid = cid; 16 } 17 public String getCustName() { 18 return custName; 19 } 20 public void setCustName(String custName) { 21 this.custName = custName; 22 } 23 public String getCustLevel() { 24 return custLevel; 25 } 26 public void setCustLevel(String custLevel) { 27 this.custLevel = custLevel; 28 } 29 public String getCustSource() { 30 return custSource; 31 } 32 public void setCustSource(String custSource) { 33 this.custSource = custSource; 34 } 35 public String getCustPhone() { 36 return custPhone; 37 } 38 public void setCustPhone(String custPhone) { 39 this.custPhone = custPhone; 40 } 41 public String getCustMobile() { 42 return custMobile; 43 } 44 public void setCustMobile(String custMobile) { 45 this.custMobile = custMobile; 46 } 47 }
第二步:讓兩個實體類之間互相表示
在客戶實體類里面表示多個聯系人(一個客戶里面多個聯系人):

1 package com.yinfu.entity; 2 3 import java.util.HashSet; 4 import java.util.Set; 5 6 public class Customer { 7 8 private Integer cid; 9 private String custName; 10 private String custLevel; 11 private String custSource; 12 private String custPhone; 13 private String custMobile; 14 15 //客戶中表示多個聯系人,一個客戶有多個聯系人 16 //Hibernate要求使用集合表示多的數據,有set表示(set無序,主要是set可有重復元素) 17 private Set<LinkMan> setLinkMan = new HashSet<LinkMan>(); 18 19 public Set<LinkMan> getSetLinkMan() { 20 return setLinkMan; 21 } 22 public void setSetLinkMan(Set<LinkMan> setLinkMan) { 23 this.setLinkMan = setLinkMan; 24 } 25 public Integer getCid() { 26 return cid; 27 } 28 public void setCid(Integer cid) { 29 this.cid = cid; 30 } 31 public String getCustName() { 32 return custName; 33 } 34 public void setCustName(String custName) { 35 this.custName = custName; 36 } 37 public String getCustLevel() { 38 return custLevel; 39 } 40 public void setCustLevel(String custLevel) { 41 this.custLevel = custLevel; 42 } 43 public String getCustSource() { 44 return custSource; 45 } 46 public void setCustSource(String custSource) { 47 this.custSource = custSource; 48 } 49 public String getCustPhone() { 50 return custPhone; 51 } 52 public void setCustPhone(String custPhone) { 53 this.custPhone = custPhone; 54 } 55 public String getCustMobile() { 56 return custMobile; 57 } 58 public void setCustMobile(String custMobile) { 59 this.custMobile = custMobile; 60 } 61 }
聯系人實體類里面表示所屬客戶(一個聯系人只能屬於一個客戶):

1 package com.yinfu.entity; 2 3 public class LinkMan { 4 5 private Integer lkm_id; 6 private String lkm_name; 7 private String lkm_gender; 8 private String lkm_phone; 9 10 //在聯系人中表示所屬客戶,一個聯系人對應一個客戶 11 private Customer customer; 12 13 public Customer getCustomer() { 14 return customer; 15 } 16 public void setCustomer(Customer customer) { 17 this.customer = customer; 18 } 19 public Integer getLkm_id() { 20 return lkm_id; 21 } 22 public void setLkm_id(Integer lkm_id) { 23 this.lkm_id = lkm_id; 24 } 25 public String getLkm_name() { 26 return lkm_name; 27 } 28 public void setLkm_name(String lkm_name) { 29 this.lkm_name = lkm_name; 30 } 31 public String getLkm_gender() { 32 return lkm_gender; 33 } 34 public void setLkm_gender(String lkm_gender) { 35 this.lkm_gender = lkm_gender; 36 } 37 public String getLkm_phone() { 38 return lkm_phone; 39 } 40 public void setLkm_phone(String lkm_phone) { 41 this.lkm_phone = lkm_phone; 42 } 43 }
第三步:配置映射關系(映射文件)
在映射文件中配置一對多關系
Customer的配置文件:

1 <?xml version="1.0" encoding="UTF-8"?> 2 <!DOCTYPE hibernate-mapping PUBLIC 3 "-//Hibernate/Hibernate Mapping DTD 3.0//EN" 4 "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> 5 <hibernate-mapping> 6 7 <class name="com.yinfu.entity.Customer" table="t_customer"> 8 <id name="cid" column="cid"> 9 <generator class="native"></generator> 10 </id> 11 <property name="custName" column="custName"></property> 12 <property name="custLevel" column="custLevel"></property> 13 <property name="custSource" column="custSource"></property> 14 <property name="custPhone" column="custPhone"></property> 15 <property name="custMobile" column="custMobile"></property> 16 <!-- 客戶配置文件中表示所有的聯系人 17 set標簽表示所有聯系人 18 set標簽中的name屬性:寫在客戶實體類里面表示所有聯系人的set集合名稱 19 --> 20 <set name="setLinkMan" cascade="save-update,delete" inverse="true"> 21 <!-- 一對多建表有外鍵 22 Hibernate機制:雙向維護外鍵,在一和多那方都配置外鍵 23 column屬性:外鍵值 24 --> 25 <key column="clid"></key> 26 <!-- 客戶表表示的所有聯系人,class表示聯系人實體類的全類名 --> 27 <one-to-many class="com.yinfu.entity.LinkMan"/> 28 </set> 29 </class> 30 31 </hibernate-mapping>
LinkMan的配置文件:

1 <?xml version="1.0" encoding="UTF-8"?> 2 <!DOCTYPE hibernate-mapping PUBLIC 3 "-//Hibernate/Hibernate Mapping DTD 3.0//EN" 4 "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> 5 <hibernate-mapping> 6 7 <class name="com.yinfu.entity.LinkMan" table="t_linkman"> 8 <id name="lkm_id" column="lkm_id"> 9 <generator class="native"></generator> 10 </id> 11 <property name="lkm_name" column="lkm_name"></property> 12 <property name="lkm_gender" column="lkm_gender"></property> 13 <property name="lkm_phone" column="lkm_phone"></property> 14 <!-- 表示聯系人所屬的客戶 15 name屬性:在聯系人實體類中用來表示客戶的字段名 16 class屬性:客戶實體類的全類名 17 column屬性:外鍵名,要與客戶映射文件中的外鍵名相同 18 --> 19 <many-to-one name="customer" class="com.yinfu.entity.Customer" column="clid"></many-to-one> 20 </class> 21 22 </hibernate-mapping>
第四步:創建核心配置文件

1 <?xml version="1.0" encoding="UTF-8"?> 2 <!DOCTYPE hibernate-configuration PUBLIC 3 "-//Hibernate/Hibernate Configuration DTD 3.0//EN" 4 "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd"> 5 <hibernate-configuration> 6 <!-- 此配置文件的文件名和位置是固定的的 7 文件名:hibernate.cfg.xml 8 位置:要寫在src文件中 9 --> 10 <session-factory> 11 <!-- 第一部分:配置數據庫信息 --> 12 <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property> 13 <property name="hibernate.connection.url">jdbc:mysql://localhost:3306/test</property> 14 <property name="hibernate.connection.username">root</property> 15 <property name="hibernate.connection.password">song12345</property> 16 17 <!-- 第二部分:配置hibernate信息(可有可無) --> 18 <!-- 輸出底層的SQL語句 --> 19 <property name="hibernate.show_sql">true</property> 20 <!-- 對底曾語句進行格式化 --> 21 <property name="hibernate.format_sql">true</property> 22 <!-- hibernate幫創建表,需要配置 23 update:如果有表就更新,沒有表就創建 24 --> 25 <property name="hibernate.hbm2ddl.auto">update</property> 26 <!-- 配置數據庫的方言 27 識別不同數據庫中的特有的語句和關鍵字 28 比如:分頁查詢 29 MySQL關鍵字是limit 30 oracle中的使用的是top-n分析中的rownum 31 --> 32 <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property> 33 <!-- 指定線程管理方式,與本地線程進行綁定,實現單線程操作 --> 34 <property name="hibernate.current_session_context_class">thread</property> 35 36 <!-- 第三部分:把映射文件放到核心配置文件中 --> 37 <mapping resource="com/yinfu/entity/Customer.hbm.xml"/> 38 <mapping resource="com/yinfu/entity/LinkMan.hbm.xml"/> 39 </session-factory> 40 41 </hibernate-configuration>
創建工具類生成SessionFactory和session對象

1 package com.yinfu.utils; 2 3 import org.hibernate.Session; 4 import org.hibernate.SessionFactory; 5 import org.hibernate.cfg.Configuration; 6 7 //工具類 8 public class HibernateUtils { 9 10 private final static Configuration cfg; 11 private final static SessionFactory sessionFactory; 12 13 //用靜態代碼塊來實現對象只在類加載的時候創建一次(靜態代碼塊只執行一次) 14 static{ 15 //創建configuration對象, 16 cfg = new Configuration(); 17 cfg.configure(); 18 //根據Configuration對象創建sessionFactory對象 19 sessionFactory = cfg.buildSessionFactory(); 20 } 21 22 //返回與本地線程綁定的session 23 public static Session getSession(){ 24 return sessionFactory.getCurrentSession(); 25 } 26 27 //創建一個方法用於返回sessionFactory對象 28 public static SessionFactory getSessionFactory(){ 29 return sessionFactory; 30 } 31 32 33 }
在工具類中直接寫一個main方法,內部不寫代碼,直接執行,生成兩個表,而外鍵是在一對多的多中的表上創建的
(一:級聯保存):
復雜的測試類:

1 package com.yinfu.test; 2 3 import java.util.List; 4 5 import org.hibernate.SQLQuery; 6 import org.hibernate.Session; 7 import org.hibernate.SessionFactory; 8 import org.hibernate.Transaction; 9 import org.junit.Test; 10 11 import com.yinfu.entity.Customer; 12 import com.yinfu.entity.LinkMan; 13 import com.yinfu.entity.User; 14 import com.yinfu.utils.HibernateUtils; 15 16 public class HibernateOneToMany { 17 18 //一對多的級聯保存 19 @Test 20 public void testAddDemo(){ 21 SessionFactory sessionFactory = null; 22 Session session = null; 23 Transaction tx = null; 24 try { 25 sessionFactory = HibernateUtils.getSessionFactory(); 26 session = sessionFactory.openSession(); 27 tx = session.beginTransaction(); 28 29 //添加客戶,為這個客戶添加一個聯系人 30 //1:創建客戶和聯系人對象 31 Customer customer = new Customer(); 32 customer.setCustName("傳智播客"); 33 customer.setCustLevel("VIP"); 34 customer.setCustSource("網絡"); 35 customer.setCustMobile("110"); 36 customer.setCustPhone("911"); 37 38 LinkMan linkMan = new LinkMan(); 39 linkMan.setLkm_name("張三"); 40 linkMan.setLkm_gender("男"); 41 linkMan.setLkm_phone("123");; 42 43 //2:客戶里面表示所有聯系人,聯系人里面表示客戶 44 //建立客戶對象和聯系人對象關系 45 //2.1:把聯系人對象放到客戶對象中的set集合里面 46 customer.getSetLinkMan().add(linkMan); 47 //2.2把客戶對象放到聯系人中的Customer屬性中 48 linkMan.setCustomer(customer); 49 50 //3:保存到數據庫 51 session.save(customer); 52 session.save(linkMan); 53 54 tx.commit(); 55 } catch (Exception e) { 56 e.printStackTrace(); 57 tx.rollback(); 58 }finally{ 59 session.close(); 60 sessionFactory.close(); 61 } 62 } 63 }
簡化做法:
首先在Customer的配置文件Customer.hbm.xml文件中的set標簽上添加一個cascade屬性值為save-update:
然后在測試類中這樣修改一下就行:
(二:級聯刪除)
刪除客戶的同時將客戶對應的聯系人全部刪除
首先在Customer的配置文件Customer.hbm.xml文件中的set標簽上添加一個cascade屬性值為delete,如果cascade屬性有多個值,用英文逗號隔開;
測試代碼:根據ID查出customer在調用delete方法就行:

1 Hibernate: 2 select 3 customer0_.cid as cid1_0_0_, 4 customer0_.custName as custName2_0_0_, 5 customer0_.custLevel as custLeve3_0_0_, 6 customer0_.custSource as custSour4_0_0_, 7 customer0_.custPhone as custPhon5_0_0_, 8 customer0_.custMobile as custMobi6_0_0_ 9 from 10 t_customer customer0_ 11 where 12 customer0_.cid=? 13 Hibernate: 14 select 15 setlinkman0_.clid as clid5_1_0_, 16 setlinkman0_.lkm_id as lkm_id1_1_0_, 17 setlinkman0_.lkm_id as lkm_id1_1_1_, 18 setlinkman0_.lkm_name as lkm_name2_1_1_, 19 setlinkman0_.lkm_gender as lkm_gend3_1_1_, 20 setlinkman0_.lkm_phone as lkm_phon4_1_1_, 21 setlinkman0_.clid as clid5_1_1_ 22 from 23 t_linkman setlinkman0_ 24 where 25 setlinkman0_.clid=? 26 Hibernate: 27 update 28 t_linkman 29 set 30 clid=null 31 where 32 clid=? 33 Hibernate: 34 delete 35 from 36 t_linkman 37 where 38 lkm_id=? 39 Hibernate: 40 delete 41 from 42 t_customer 43 where 44 cid=?
(一對多修改操作)
執行結果底層的SQL語句:(這樣存在雙向維護外鍵,存在性能不足)

1 Hibernate: 2 select 3 customer0_.cid as cid1_0_0_, 4 customer0_.custName as custName2_0_0_, 5 customer0_.custLevel as custLeve3_0_0_, 6 customer0_.custSource as custSour4_0_0_, 7 customer0_.custPhone as custPhon5_0_0_, 8 customer0_.custMobile as custMobi6_0_0_ 9 from 10 t_customer customer0_ 11 where 12 customer0_.cid=? 13 Hibernate: 14 select 15 linkman0_.lkm_id as lkm_id1_1_0_, 16 linkman0_.lkm_name as lkm_name2_1_0_, 17 linkman0_.lkm_gender as lkm_gend3_1_0_, 18 linkman0_.lkm_phone as lkm_phon4_1_0_, 19 linkman0_.clid as clid5_1_0_ 20 from 21 t_linkman linkman0_ 22 where 23 linkman0_.lkm_id=? 24 Hibernate: 25 select 26 setlinkman0_.clid as clid5_1_0_, 27 setlinkman0_.lkm_id as lkm_id1_1_0_, 28 setlinkman0_.lkm_id as lkm_id1_1_1_, 29 setlinkman0_.lkm_name as lkm_name2_1_1_, 30 setlinkman0_.lkm_gender as lkm_gend3_1_1_, 31 setlinkman0_.lkm_phone as lkm_phon4_1_1_, 32 setlinkman0_.clid as clid5_1_1_ 33 from 34 t_linkman setlinkman0_ 35 where 36 setlinkman0_.clid=? 37 Hibernate: 38 下面進行了兩次修改,由於Hibernate是雙向維護外鍵,所以,客戶和聯系人中都要進行外鍵維護,存在性能不足 39 update 40 t_linkman 41 set 42 lkm_name=?, 43 lkm_gender=?, 44 lkm_phone=?, 45 clid=? 46 where 47 lkm_id=? 48 Hibernate: 49 update 50 t_linkman 51 set 52 clid=? 53 where 54 lkm_id=?
性能優化,解決方法,在一對多中讓一放棄外鍵維護,即讓customer放棄外鍵維護
具體實現:
在需要放棄外鍵的對象的映射文件中的set標簽中進行配置,添加inverse屬性:默認值是false,修改為true,就是放棄外鍵維護
底層SQL的變化: