根據我們的總結計划,上篇文章我們總結了有關於一對一映射相關知識,接下來,我們進行下一個階段,一對多、多對一映射相關知識。
場景設定:
國家規定,一個人只能在一個公司上班,一個公司可以擁有很多員工。我們就利用這個場景,根據針對對象的不同,我們分別來分析一下一對多、多對一關聯映射。
一、多對一單向關聯映射
1、多對一單向關聯映射:對於員工(Employee)來說,他跟公司(Company)的對應關系就是多對一關系
Po對象:Employee.Java
- public class Employee {
- public int id;
- public String name;
- public Company company;
-
- }
Company.java
- public class Company{
- public int id;
- public String name;
-
- }
作為程序員,我們都知道在設計數據庫要在多對一的多那一面添加一的外鍵,所以我們在員工類中,添加對公司類的引用。
映射文件:Employee.hbm.xml
- <hibernate-mapping package="org.hibernate.test" >
- <class name="com.ssh.hibernate.Employee" table="t_employee">
- <id name="id" type="int">
- <generator class="native"/>
- </id>
- <property name="name"/>
- <many-to-one name="company" column="companyid"/>
- </class>
- </hibernate-mapping>
Company.hbm.xml
- <hibernate-mapping package="org.hibernate.test" >
- <class name="com.ssh.hibernate.Company" table="t_company">
- <id name="id">
- <generator class="native"/>
- </id>
- <property name="name"/>
- </class>
- </hibernate-mapping>
執行程序自動生成表:
- create table t_company (id integer not null auto_increment, name varchar(255), primary key (id))
- create table t_employee (id integer not null auto_increment, name varchar(255), companyid integer, primary key (id))
測試:
- session.beginTransaction();
- Company company=new Company();
- company.setName("某某集團");
- session.save(company);
- Employee employee1=new Employee();
- employee1.setName("路人甲");
- employee1.setCompany(company);
- Employee employee2=new Employee();
- employee2.setName("路人乙");
- employee2.setCompany(company);
- session.save(employee1);
- session.save(employee2);
- session.getTransaction().commit();
執行結果:
- Hibernate: insert into Company (id,name) values (?,?)
- Hibernate: insert into Employee (id,name,companyid) values (?,?,?)
值得一提的是,如果我們沒有在測試程序里面session.save(company),直接執行程序,我們會報錯,但是解決辦法絕不是只有這一種,我們還可以在員工Employee映射文件中的<many-to-one/>中配置cascade屬性:
- <hibernate-mapping package="org.hibernate.test" >
- <class name="com.ssm.hibernate.Employee" table="t_employee">
- <id name="id" type="int">
- <generator class="native"/>
- </id>
- <property name="name"/>
- <many-to-one name="company" column="companyid" cascade="save-update"/>
-
- </class>
- </hibernate-mapping>
關於cascade的一些屬性值分別是:persist, merge, delete, save-update, evict, replicate, lock, refresh
二、一對多單向關聯映射:
同樣適用上面的場景設定:國家規定一個員工只能在一個公司上班,但是一個公司可以擁有很多員工。這時候,針對公司來說,就是一對多關系了。像這種時候,我們就需要在公司類中添加一個對員工對象的集合了。這個集合可以是set、list、map、array數組的有關容器(其中set中的對象不可重復,相對性能也比較高,建議使用set)
Po對象:Employee.java
- public class Employee{
- public int id;
- public String name;
-
- }
Company.java
- public class Company{
- public int id;
- public String name;
- public Set<Employee> employees;
-
- }
映射文件:Employee.hbm.xml
- <hibernate-mapping package="org.hibernate.test" >
- <class name="com.ssh.hibernate.Employee" table="t_employee">
- <id name="id" type="int">
- <generator class="native"/>
- </id>
- <property name="name"/>
- </class>
- </hibernate-mapping>
Company.hbm.xml
- <hibernate-mapping package="org.hibernate.test" >
- <class name="com.ssh.hibernate.Company" table="t_company">
- <id name="id">
- <generator class="native"/>
- </id>
- <property name="name"/>
- <set name=employees>
- <key column="companyid"></key>
- <one-to-many class="com.ssh.hibernate.Employee"/>
- </set>
- </class>
- </hibernate-mapping>
測試:
- session.beginTransaction();
- Employee employee1=new Employee();
- employee1.setName("路人甲");
- session.save(employee1)
- Employee employee2=new Employee();
- employee2.setName("路人乙");
- employee2.save(employee2);
- Set<Employee> employees=new HashSet<Employee>();
- employees.add(employee1);
- employees.add(employee2);
- Company company=new Company();
- company.setName("某某集團");
- company.setEmployees(employees);
- session.save(company);
- session.getTransaction().commit();
事務提交數據插入之后,我們進行查詢:
- session.beginTransaction();
- Company company=(Company)session.load(Company.class,1);
- System.out.println("公司名稱:"+company.getName());
- System.out.println("公司員工:");
- for(Employee employee:company.getEmployees()){
- System.out.print(" "+employee.getName());
- }
- session.getTransaction().commit();
查詢結果:
- Hibernate: select company0_.id as id0_0_, company0_.name as name0_0_ from t_company company0_
- where company0_.id=?
- 公司名稱:某某集團
- 公司員工:Hibernate: select employees0_.companyid as company3_1_, employees0_.id as id1_,
- employees0_.id as id1_0_,employees0_.name as name1_0_ from t_employee employees0_
- where employees0_.companytid=?
- 路人甲 路人乙
從控制台消息來看,還能延遲加載lazy,那如果我們把配置文件改為:
Company.hbm.xml
- <hibernate-mapping package="org.hibernate.test" >
- <class name="com.ssh.hibernate.Company" table="t_company">
- <id name="id">
- <generator class="native"/>
- </id>
- <property name="name"/>
- <set name=employees lazy="false">
- <key column="companyid"></key>
- <one-to-many class="com.ssh.hibernate.Employee"/>
- </set>
- </class>
- </hibernate-mapping>
三、多對一/一對多雙向關聯映射
現在我們還是用上面的場景設定來實現一對多/多對一雙向關聯:
Po對象:Company.java
- public class Company{
- public int id;
- public String name;
- public Set<Employee> employees;
-
- }
Employee.java
- public class Employee {
- public int id;
- public String name;
- public Company company;
-
- }
配置文件:Employee.hbm.xml
- <hibernate-mapping package="org.hibernate.test" >
- <class name="com.ssh.hibernate.Employee" table="t_employee">
- <id name="id" type="int">
- <generator class="native"/>
- </id>
- <property name="name"/>
- <many-to-one name="company" column="companyid" not-null="true">
- </class>
- </hibernate-mapping>
Company.hbm.xml
- <hibernate-mapping package="org.hibernate.test" >
- <class name="com.ssh.hibernate.Company" table="t_company">
- <id name="id">
- <generator class="native"/>
- </id>
- <property name="name"/>
- <set name="employees">
- <key column="companyid"></key>
- <one-to-many class="com.ssh.hibernate.Employee"/>
- </set>
- </class>
- </hibernate-mapping>
如果你使用List(或者其他有序集合類),你需要設置外鍵對應的key列為 not null,讓Hibernate來從集合端管理關聯,維護每個元素的索引(通過設置update="false" and insert="false"來對另一端反向操作):
Employee.hbm.xml
- <hibernate-mapping package="org.hibernate.test" >
- <class name="com.ssh.hibernate.Employee" table="t_employee">
- <id name="id" type="int">
- <generator class="native"/>
- </id>
- <property name="name"/>
- <many-to-one name="company" column="companyid" not-null="true" insert="flase" update="false"/>
- </class>
- </hibernate-mapping>
Company.hbm.xml
- <hibernate-mapping package="org.hibernate.test" >
- <class name="com.ssh.hibernate.Company" table="t_company">
- <id name="id">
- <generator class="native"/>
- </id>
- <property name="name"/>
- <list name="employees">
- <key column="companyid" not-null="true"></key>
- <list-index column="employeeId"/>
- <one-to-many class="com.ssh.hibernate.Employee"/>
- </set>
- </class>
- </hibernate-mapping>
假若集合映射的<key>元素對應的底層外鍵字段是NOT NULL的,那么為這一key元素定義not-null="true"是很重要的。不要僅僅為可能的嵌套<column>元素定義not-null="true",<key>元素也是需要的。
四、總結:
1、對於單向的一對多、多對一關聯映射,建表時,都是在“多”的一端添加外鍵指向“一”的一端。而他們的不同點就是維護關系的不同,也可理解為主表變更,由誰指向誰的關系變了。
2、對於雙向的一對多/多對一來說,他們之間本就是互為指向的,只是要注意我們需用的方法的不同來針對不同的地方進行配置。使用set、list的時候,大體上是差不多的,關鍵就是使用list的時候,多對一從表的逐漸不可自己更添,而一對多從表主/外鍵id不能為空