三、1—N
對於1-N而言,它的持久化類發生瞭一點改變,持久化類裡需要使用集合屬性。因為1的一端需要訪問N的一端,而N的一端將以集合(Set)形式表現。
1、單向1-N關聯
對於單向的1-N關聯關系,隻需要在1的一端增加Set類型的屬性,該屬性記錄當前實體的關聯實體。
同樣以員工-部門為例(Employee–>Department)。兩個持久化類如下:
Department
[java]
public class Department {
private Integer id;
private String name;
private Set<Employee> employees; //關聯關系
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Set<Employee> getEmployees() {
return employees;
}
public void setEmployees(Set<Employee> employees) {
this.employees = employees;
}
}
Employee
[java]
public class Employee {
private Integer id;
private String name;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
1.1基於無連接表的單向1-N關聯
對於1-N的單向關聯,需要在1的一端增加對應的集合映射元素,如<set…/>、<bag…/>。在集合元素中需要增加<key…/>子元素,該子元素用來映射外鍵。同時集合元素中需要使用<one-to-many…/>元素來映射1-N關聯關系。
下面是Department的映射文件Department.hbm.xml
[html]
<hibernate-mapping package="com.hibernate.domain" >
<class name="Department" table="department">
<id name="id" column="departmentID">
<generator class="native" />
</id>
<property name="name" column="departmentName" />
<!– 映射集合屬合 –>
<set name="employees" inverse="true" >
<!– 指定關聯的外鍵列 –>
<key column="departmentID" />
<!– 用以映射到關聯類屬性 –>
<one-to-many class="Employee"/>
</set>
</class>
</hibernate-mapping>
對於上面的映射文件,映射<set…/>元素時並沒有指定cascade屬性,在默認的情況下,對主表實體的持久化操作不會級聯到從表實體。
Employee.hbm.xml
[html]
<hibernate-mapping package="com.hibernate.domain">
<class name="Employee" table="employee">
<id name="id" column="employeeID">
<generator class="native" />
</id>
<property name="name" column="employeeName" />
</class>
</hibernate-mapping>
使用下面代碼來操作Department和Employee實體:保存一個Department實體和兩個Employee實體
[java]
static void add(){
Session session = HibernateUtil.getSession();
Transaction tx = session.beginTransaction();
Department department = new Department();
department.setName("國防部");
//建立兩個對象
Employee employee1 = new Employee();
employee1.setName("chentmt1");
Employee employee2 = new Employee();
employee2.setName("chentmt2");
Set<Employee> emps = new HashSet<Employee>();
emps.add(employee1);
emps.add(employee2);
//設置Department和Employee之間的關聯關系
department.setEmployees(emps);
session.save(department); //….1
session.save(employee2);
session.save(employee1);
tx.commit();
session.close();
}
分析上面代碼段:
當程序運行到….1的時候,系統會持久化該對象:Department,而且這個對象已經關聯瞭兩個Employee對象。在這個時候Hibernate需要完成兩件事:
1、執行insert語句想department表中插入一條記錄
2、Hibernate試圖執行update語句,將當前的department表記錄關聯的employee表記錄的外鍵departmentID修改為該department記錄的主鍵的值。
下面為上面代碼段的sql語句:
[sql]
Hibernate: insert into department (departmentName) values (?)
Hibernate: insert into employee (employeeName) values (?)
Hibernate: insert into employee (employeeName) values (?)
Hibernate: update employee set departmentID=? where employeeID=?
Hibernate: update employee set departmentID=? where employeeID=?
從上面的sql語句中我們可以看到雖然程序僅僅需要為Department實體增加一個關聯Employee實體,但是Hibernate會采用兩條SQL語句來完成:一條inset語句插入一個條外鍵為null的employee記錄,一條update語句修改插入的employee記錄。造成這個問題的根本原因就在於:從Department到Employee的關聯並沒有被當中為Department對象狀態的一部分(程序是通過吧Employee實體添加到Department實體的集合屬性中,而Employee實體並不知道她所關聯的Department實體),因而Hibernate無法在執行insert語句為該外鍵指定值。
為瞭解決這個問題,程序必須在持久化Employee實體之前,讓Employee實體能夠知道它所關聯的Department實體,也就是通過employee.setDepartment(department);方法建立關聯關系,但是這個時候就已經變成瞭1-N的雙向關聯。所以,盡量少用1-N的單向關聯,而改用1-N的雙向關聯。
1.2基於有連接表的單向1-N關聯
對於有連接表的1-N關聯映射,映射文件不再使用<one-to-many…./>元素映射關聯實體,而是使用<many-to-many…/>元素,但是為瞭保證當前實體是1的一端,我們需要增加一個屬性:unique="true"。
所以Department.hbm.xml配置文件如下:
[html]
<hibernate-mapping package="com.hibernate.domain" >
<class name="Department" table="department">
<id name="id" column="departmentID">
<generator class="native" />
</id>
<property name="name" column="departmentName" />
<set name="employees" inverse="true" table="department_employee">
<!– 指定關聯的外鍵列 –>
<key column="departmentID" />
<!– 用以映射到關聯類屬性 –>
<many-to-many class="Employee" column="employeeID" unique="true"/>
</set>
</class>
</hibernate-mapping>
Employee.hbm.xml配置文件保持不變
如果我們依然通過使用操作方法:保存一個Department、兩個Employee,這個時候,系統應該是產生5條sql語句。其中兩條是用於向連接表中插入記錄,從而建立Department和Employee之間的關聯關系。
2、雙向1-N關聯
對於1-N關聯,Hibernate推薦使用雙向關聯,而且不要讓1端控制關聯關系,而使用N的一端控制關聯關系。
雙向的1-N關聯,兩端都需要增加對關聯屬性的訪問,N的一端增加引用到關聯實體的屬性,1的一端增加集合屬性,集合元素為關聯實體。
兩個持久化類和上面差不多,隻需要在Employee中增加對Department的引用屬性即可。
2.1無連接表的雙向1-N關聯
對於無連接表的雙向1-N關聯。N的一端需要增加<many-to-one…/>元素來映射關聯屬性,而1的一端需要使用<set…/>或者<bag…/>元素來映射關聯屬性。同時<set…/>或者<bag../>元素需要增加<key…/>子元素映射外鍵列,並且使用<one-to-many…/>子元素映射關聯屬性。
在前面已經提到對於1-N關聯映射,通常不提倡1的一端控制關聯關系,而應該由N的一端來控制關聯關系。此時我們可以再<set…/>元素中指定inverse="true",用於指定1的一端不控制關聯關系
Department映射文件:Department.hbm.xml
[html]
<hibernate-mapping package="com.hibernate.domain" >
<class name="Department" table="department">
<id name="id" column="departmentID">
<generator class="native" />
</id>
<property name="name" column="departmentName" />
<!– 映射集合屬合 –>
<set name="employees" inverse="true" >
<!– 指定關聯的外鍵列 –>
<key column="departmentID" />
<!– 用以映射到關聯類屬性 –>
<one-to-many class="Employee"/>
</set>
</class>
</hibernate-mapping>
Employee映射文件:Employee.hbm.xml
[html] view plaincopyprint?
<hibernate-mapping package="com.hibernate.domain">
<class name="Employee" table="employee">
<id name="id" column="employeeID">
<generator class="native" />
</id>
<property name="name" column="employeeName" />
<!– 用於映射N-1關聯實體,指定關聯實體類為 :Department,指定外鍵為:departmentID–>
<many-to-one name="department" column="departmentID" />
</class>
</hibernate-mapping>
下面的程序段用於保存一個Department對象和兩個Employee對象
[java]
static void add(){
Session session = HibernateUtil.getSession();
Transaction tx = session.beginTransaction();
Department department = new Department();
department.setName("國防部");
//建立兩個對象
Employee employee1 = new Employee();
employee1.setName("chentmt1");
employee1.setDepartment(department); //建立兩個對象的關聯關系
Employee employee2 = new Employee();
employee2.setName("chentmt2");
employee2.setDepartment(department); //建立兩個對象的關聯關系
session.save(department); //….1
session.save(employee2);
session.save(employee1);
tx.commit();
session.close();
}
SQL語句:
[sql]
Hibernate: insert into department (departmentName) values (?)
Hibernate: insert into employee (employeeName, departmentID) values (?, ?)
Hibernate: insert into employee (employeeName, departmentID) values (?, ?)
通過上面的SQL語句可以看出,Hibernate並不是采用哪種先insert後update的方式來插入employee記錄的。而是通過一條insert SQL語句來執行的。為什麼?因為程序持久化Employee實體之前,Employee已經知道它所關聯Department實體(employee2.setDepartment(department);)。 所以為瞭保證比較好的性能,需要註意以下兩個問題:
1、應該先持久化主表對象:Department。因為我們希望程序在持久化從表:Employee對象時,Hibernate可以為他的外鍵屬性值分配值。
2、先設置兩個持久化類(Department和Employee)的關系,再保存持久化從表對象(Employee)。如果順序反過來,程序持久化Employee對象時,該對象還沒有關聯實體,所以Hibernate不能為對應記錄的外鍵列指定值,等到設置關聯關系時,Hibernate隻能再次使用update語句來修改瞭。
2.2有連接表的雙向1-N關聯
有連接表的雙向1-N關聯。1的一端使用集合元素映射,然後在集合元素中增加<many-to-many../>子元素,該子元素映射到關聯類。為保證該實體是1的一端,需要增加unique="true"屬性。N的一端則使用<join…/>元素來強制使用連接表。
在N的一端使用<join../>元素強制使用連接表,因此將使用<key…/>子元素來映射連接表中外鍵列,器且使用<many-to-one../>來映射連接表中關聯實體的外鍵列;反過來,1的一端也將在<set…/>元素中使用<key…/>和<many-to-many…/>兩個子元素,他們也映射到連接表的兩列。為瞭保證得到正確的映射關系,應該滿足如下關系:<join…/>裡<key…/>子元素的column屬性和<set…/>裡<man-to-many…/>元素的column屬性相同;<join…/>裡<many-to-many…/>子元素的column屬性和<set../>元素的<key../>子元素的column屬性應該相等。
下面就是Department和Employee兩個持久化類的映射文件:
Department.hbm.xml
[html]
<hibernate-mapping package="com.hibernate.domain" >
<class name="Department" table="department">
<id name="id" column="departmentID">
<generator class="native" />
</id>
<property name="name" column="departmentName" />
<set name="employees" inverse="true" table="department_employee">
<!– 指定關聯的外鍵列 –>
<key column="departmentID" />
<!– 用以映射到關聯類屬性 –>
<many-to-many class="Employee" column="employeeID" unique="true"/>
</set>
</class>
</hibernate-mapping>
Employee.hbm.xml
[html]
<hibernate-mapping package="com.hibernate.domain">
<class name="Employee" table="employee">
<id name="id" column="employeeID">
<generator class="native" />
</id>
<property name="name" column="employeeName" />
<!– 使用join元素強制使用連接表 –>
<join table="department_employee">
<!– key映射外鍵列 –>
<key column="employeeID" />
<!– 映射關聯實體 –>
<many-to-one name="department" column="departmentID" />
</join>
</class>
</hibernate-mapping>
作者:chenssy