【Hibernate框架】關聯映射(一對多,多對一)


根據我們的總結計划,上篇文章我們總結了有關於一對一映射相關知識,接下來,我們進行下一個階段,一對多、多對一映射相關知識。

 

場景設定:

       國家規定,一個人只能在一個公司上班,一個公司可以擁有很多員工。我們就利用這個場景,根據針對對象的不同,我們分別來分析一下一對多、多對一關聯映射。

 

一、多對一單向關聯映射

1、多對一單向關聯映射:對於員工(Employee)來說,他跟公司(Company)的對應關系就是多對一關系

Po對象:Employee.Java

 

[java]  view plain copy
 print?在CODE上查看代碼片派生到我的代碼片
  1. public class Employee {  
  2.     public int id;  
  3.     public String name;  
  4.     public Company company;  
  5.     //getter、setter  
  6. }  
Company.java

 

 

[java]  view plain copy
 print?在CODE上查看代碼片派生到我的代碼片
  1. public class Company{  
  2.     public int id;  
  3.     public String name;  
  4.     //getter、setter  
  5. }  
       作為程序員,我們都知道在設計數據庫要在多對一的多那一面添加一的外鍵,所以我們在員工類中,添加對公司類的引用。

 

映射文件:Employee.hbm.xml

 

[html]  view plain copy
 print?在CODE上查看代碼片派生到我的代碼片
  1. <hibernate-mapping package="org.hibernate.test" >  
  2.     <class name="com.ssh.hibernate.Employee" table="t_employee">  
  3.             <id name="id" type="int">  
  4.                 <generator class="native"/>  
  5.             </id>  
  6.             <property name="name"/>  
  7.             <many-to-one name="company" column="companyid"/>  
  8.     </class>  
  9. </hibernate-mapping>    
Company.hbm.xml

 

 

[html]  view plain copy
 print?在CODE上查看代碼片派生到我的代碼片
  1. <hibernate-mapping package="org.hibernate.test" >  
  2.     <class name="com.ssh.hibernate.Company" table="t_company">  
  3.             <id name="id">  
  4.                 <generator class="native"/>  
  5.             </id>  
  6.             <property name="name"/>  
  7.     </class>  
  8. </hibernate-mapping>  
執行程序自動生成表:

 

 

[sql]  view plain copy
 print?在CODE上查看代碼片派生到我的代碼片
  1. create table t_company (id integer not null auto_increment, name varchar(255), primary key (id))  
  2. create table t_employee (id integer not null auto_increment, name varchar(255), companyid integer, primary key (id))  

 

測試

 

[java]  view plain copy
 print?在CODE上查看代碼片派生到我的代碼片
  1. session.beginTransaction();  
  2. Company company=new Company();  
  3. company.setName("某某集團");  
  4. session.save(company);//這里必須要先save一下company,將之變成持久化狀態否則會因為無法保存瞬時對象而報錯  
  5. Employee employee1=new Employee();  
  6. employee1.setName("路人甲");  
  7. employee1.setCompany(company);  
  8. Employee employee2=new Employee();  
  9. employee2.setName("路人乙");  
  10. employee2.setCompany(company);  
  11. session.save(employee1);  
  12. session.save(employee2);  
  13. session.getTransaction().commit();  
執行結果:

 

 

[sql]  view plain copy
 print?在CODE上查看代碼片派生到我的代碼片
  1. Hibernate: insert into Company (id,name) values (?,?)  
  2. Hibernate: insert into Employee (id,name,companyid) values (?,?,?)  

 

值得一提的是,如果我們沒有在測試程序里面session.save(company),直接執行程序,我們會報錯,但是解決辦法絕不是只有這一種,我們還可以在員工Employee映射文件中的<many-to-one/>中配置cascade屬性:

 

[html]  view plain copy
 print?在CODE上查看代碼片派生到我的代碼片
  1. <hibernate-mapping package="org.hibernate.test" >  
  2.     <class name="com.ssm.hibernate.Employee" table="t_employee">  
  3.             <id name="id" type="int">  
  4.                 <generator class="native"/>  
  5.             </id>  
  6.             <property name="name"/>  
  7.             <many-to-one name="company" column="companyid" cascade="save-update"/>  
  8.              <!--在這里配置cascade屬性,表示兩個對象之間的操作為聯動關系-->  
  9.     </class>  
  10. </hibernate-mapping>  
關於cascade的一些屬性值分別是:persist, merge, delete, save-update, evict, replicate, lock, refresh

 

 

二、一對多單向關聯映射:

       同樣適用上面的場景設定:國家規定一個員工只能在一個公司上班,但是一個公司可以擁有很多員工。這時候,針對公司來說,就是一對多關系了。像這種時候,我們就需要在公司類中添加一個對員工對象的集合了。這個集合可以是set、list、map、array數組的有關容器(其中set中的對象不可重復,相對性能也比較高,建議使用set)

Po對象:Employee.java

 

[java]  view plain copy
 print?在CODE上查看代碼片派生到我的代碼片
  1. public class Employee{  
  2.     public int id;  
  3.     public String name;  
  4.     //getter、setter  
  5. }  

 

Company.java

 

[java]  view plain copy
 print?在CODE上查看代碼片派生到我的代碼片
  1. public class Company{  
  2.     public int id;  
  3.     public String name;  
  4.     public Set<Employee> employees;  
  5.     //getter、setter  
  6. }  

 

映射文件:Employee.hbm.xml

 

[html]  view plain copy
 print?在CODE上查看代碼片派生到我的代碼片
  1. <hibernate-mapping package="org.hibernate.test" >  
  2.     <class name="com.ssh.hibernate.Employee" table="t_employee">  
  3.             <id name="id" type="int">  
  4.                 <generator class="native"/>  
  5.             </id>  
  6.             <property name="name"/>  
  7.     </class>  
  8. </hibernate-mapping>  

 

Company.hbm.xml

 

[html]  view plain copy
 print?在CODE上查看代碼片派生到我的代碼片
  1. <hibernate-mapping package="org.hibernate.test" >  
  2.     <class name="com.ssh.hibernate.Company" table="t_company">  
  3.             <id name="id">  
  4.                 <generator class="native"/>  
  5.             </id>  
  6.             <property name="name"/>  
  7.             <set name=employees>  
  8.         <key column="companyid"></key><!-- "多"的一方關聯"一"的一方的外鍵 -->  
  9.         <one-to-many class="com.ssh.hibernate.Employee"/><!-- 一個Company對象對應多個Employee對象 -->  
  10.         </set>  
  11.     </class>  
  12. </hibernate-mapping>  

 

測試:

 

[java]  view plain copy
 print?在CODE上查看代碼片派生到我的代碼片
  1. session.beginTransaction();  
  2. Employee employee1=new Employee();  
  3. employee1.setName("路人甲");  
  4. session.save(employee1)  
  5. Employee employee2=new Employee();  
  6. employee2.setName("路人乙");  
  7. employee2.save(employee2);  
  8. Set<Employee> employees=new HashSet<Employee>();  
  9. employees.add(employee1);  
  10. employees.add(employee2);  
  11. Company company=new Company();  
  12. company.setName("某某集團");  
  13. company.setEmployees(employees);  
  14. session.save(company);  
  15. session.getTransaction().commit();  
事務提交數據插入之后,我們進行查詢:

 

 

[java]  view plain copy
 print?在CODE上查看代碼片派生到我的代碼片
  1. session.beginTransaction();  
  2. Company company=(Company)session.load(Company.class,1);  
  3. System.out.println("公司名稱:"+company.getName());  
  4. System.out.println("公司員工:");  
  5. for(Employee employee:company.getEmployees()){  
  6.     System.out.print(" "+employee.getName());  
  7. }  
  8. session.getTransaction().commit();  

 

查詢結果:

 

[sql]  view plain copy
 print?在CODE上查看代碼片派生到我的代碼片
  1. Hibernate: select company0_.id as id0_0_, company0_.name as name0_0_ from t_company company0_   
  2.            where company0_.id=?  
  3. 公司名稱:某某集團  
  4. 公司員工:Hibernate: select employees0_.companyid as company3_1_, employees0_.id as id1_,   
  5.            employees0_.id as id1_0_,employees0_.name as name1_0_ from t_employee employees0_   
  6.            where employees0_.companytid=?  
  7. 路人甲 路人乙  
從控制台消息來看,還能延遲加載lazy,那如果我們把配置文件改為:

 

 

Company.hbm.xml

[html]  view plain copy
 print?在CODE上查看代碼片派生到我的代碼片
  1. <hibernate-mapping package="org.hibernate.test" >  
  2.    <class name="com.ssh.hibernate.Company" table="t_company">  
  3.        <id name="id">  
  4.           <generator class="native"/>  
  5.        </id>  
  6.        <property name="name"/>  
  7.        <set name=employees lazy="false"><!--如果這里將lazy設置成false,將禁止延遲加載,默認為true-->  
  8.        <key column="companyid"></key><!-- "多"的一方關聯"一"的一方的外鍵 -->  
  9.        <one-to-many class="com.ssh.hibernate.Employee"/><!-- 一個Company對象對應多個Employee對象 -->  
  10.        </set>  
  11.   </class>  
  12. </hibernate-mapping>  

 

 

三、多對一/一對多雙向關聯映射

 

現在我們還是用上面的場景設定來實現一對多/多對一雙向關聯:

Po對象:Company.java

 

[java]  view plain copy
 print?在CODE上查看代碼片派生到我的代碼片
  1. public class Company{  
  2.     public int id;  
  3.     public String name;  
  4.     public Set<Employee> employees;  
  5.     //getter、setter  
  6. }  
Employee.java

 

 

[java]  view plain copy
 print?在CODE上查看代碼片派生到我的代碼片
  1. public class Employee {  
  2.     public int id;  
  3.     public String name;  
  4.     public Company company;  
  5.     //getter、setter  
  6. }  

 

配置文件:Employee.hbm.xml

 

[html]  view plain copy
 print?在CODE上查看代碼片派生到我的代碼片
  1. <hibernate-mapping package="org.hibernate.test" >  
  2.     <class name="com.ssh.hibernate.Employee" table="t_employee">  
  3.             <id name="id" type="int">  
  4.                 <generator class="native"/>  
  5.             </id>  
  6.             <property name="name"/>  
  7.             <many-to-one name="company" column="companyid" not-null="true">  
  8.     </class>  
  9. </hibernate-mapping>  

 

 

Company.hbm.xml
[html]  view plain copy
 print?在CODE上查看代碼片派生到我的代碼片
  1. <hibernate-mapping package="org.hibernate.test" >  
  2.     <class name="com.ssh.hibernate.Company" table="t_company">  
  3.             <id name="id">  
  4.                 <generator class="native"/>  
  5.             </id>  
  6.             <property name="name"/>  
  7.             <set name="employees">  
  8.             <key column="companyid"></key>  
  9.             <one-to-many class="com.ssh.hibernate.Employee"/>  
  10.             </set>  
  11.     </class>  
  12. </hibernate-mapping>  
       如果你使用List(或者其他有序集合類),你需要設置外鍵對應的key列為 not null,讓Hibernate來從集合端管理關聯,維護每個元素的索引(通過設置update="false" and insert="false"來對另一端反向操作): 

Employee.hbm.xml

 

[html]  view plain copy
 print?在CODE上查看代碼片派生到我的代碼片
  1. <hibernate-mapping package="org.hibernate.test" >  
  2.     <class name="com.ssh.hibernate.Employee" table="t_employee">  
  3.             <id name="id" type="int">  
  4.                 <generator class="native"/>  
  5.             </id>  
  6.             <property name="name"/>  
  7.         <many-to-one name="company" column="companyid" not-null="true" insert="flase" update="false"/>  
  8.     </class>  
  9. </hibernate-mapping>  
Company.hbm.xml
[html]  view plain copy
 print?在CODE上查看代碼片派生到我的代碼片
  1. <hibernate-mapping package="org.hibernate.test" >  
  2.     <class name="com.ssh.hibernate.Company" table="t_company">  
  3.             <id name="id">  
  4.                 <generator class="native"/>  
  5.             </id>  
  6.             <property name="name"/>  
  7.             <list name="employees">  
  8.             <key column="companyid" not-null="true"></key>  
  9.             <list-index column="employeeId"/>  
  10.             <one-to-many class="com.ssh.hibernate.Employee"/>  
  11.             </set>  
  12.     </class>  
  13. </hibernate-mapping>  

 

       假若集合映射的<key>元素對應的底層外鍵字段是NOT NULL的,那么為這一key元素定義not-null="true"是很重要的。不要僅僅為可能的嵌套<column>元素定義not-null="true",<key>元素也是需要的。

 

四、總結:

1、對於單向的一對多、多對一關聯映射,建表時,都是在“多”的一端添加外鍵指向“一”的一端。而他們的不同點就是維護關系的不同,也可理解為主表變更,由誰指向誰的關系變了。

2、對於雙向的一對多/多對一來說,他們之間本就是互為指向的,只是要注意我們需用的方法的不同來針對不同的地方進行配置。使用set、list的時候,大體上是差不多的,關鍵就是使用list的時候,多對一從表的逐漸不可自己更添,而一對多從表主/外鍵id不能為空


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM