我們所生活的世界很少有事物是孤立存在的,每個事物必然會存在著與它相關聯的事物。在面向對象設計當中,關聯關系是非常重要的。關聯關系一般可以分為以下兩種:
單向關系:隻需單向訪問關聯端
雙向關系:關聯的兩端都可以互相訪問
單向關系可分為:1—N、1—1、N—1、N—N
雙向關系可分為:1—1 、1—N、N—N
下面就上面的每種關聯映射分別講解:
一、N—1關聯映射
1、單向N—1關聯
1.1、無連接表的N-1關聯(基於外鍵的N-1關聯)
對於單向的N—1關聯而言隻需要從N的一端可以訪問1的一端。為瞭讓這個兩個持久化類支持這種關聯映射,程序應該在N的一端的持久化類中增加一個熟悉,該屬性引用1一端的關聯實體。
兩個關聯屬性如下(以員工和部門之間的關系為例):
Employee:
[java]
public class Employee {
private Integer id;
private String name;
private Department department; //關聯屬性
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 Department getDepartment() {
return department;
}
public void setDepartment(Department department) {
this.department = 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端增加瞭Department屬性,該屬性並不是一個普通的組件屬性,而是引用另一個持久化類的類。Hibernate使用<many-to-one…/>元素映射N—1的關聯實體,直接采用<many-to-one…/>元素來映射關聯實體將會在N的一端的數據表中增加一個外鍵,用於參照主表記錄。
下面為兩個實體的映射文件:
Employee.hbm.xml
在這個映射文件中需要用<many-to-one../>來完成關聯映射
[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 class="Department" name="department" column="departmentID" not-null="true" cascade="all"/>
</class>
</hibernate-mapping>
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" />
</class>
</hibernate-mapping>
通過上面的映射後,就可以使用如下代碼來保存Employee和Department實體瞭。
[java] view plaincopyprint?
//增加
static void add() {
Session s = null;
Transaction tx = null;
try {
s = HibernateUtil.getSession();
tx = s.beginTransaction();
Department depart = new Department(); //….1
depart.setName("組織部");
Employee emp = new Employee();
//對象模型:建立兩個對象的關聯關系
emp.setDepartment(depart); //…2
emp.setName("湯妹彤");
s.save(depart); //…3
s.save(emp); //…4
tx.commit();
} finally {
if (s != null)
s.close();
}
}
代碼分析:
當代碼運行到…1的時候會創建一個瞬態的Department對象。當程序運行到…3和…4的時候,系統就會分別保存Department對象和Employee對象。會產生如下兩條SQL語句:
[sql]
Hibernate: insert into department (departmentName) values (?)
e: insert into employee (employeeName, departmentID) values (?, ?)
…2這條語句非常重要,因為它是建立Department和Employee兩個對象的關聯關系。沒有這條語句是無法建立兩個對象的關系。
在這裡我們將…3語句和…4兩條語句交換位置。這時運行就會產生三條SQL語句。當…3的時候,departmentID插入的時候為空,隻有當持久化Department對象後,系統就會將departmentID修改為相對應的值。如下:
[sql]
Hibernate: insert into employee (employeeName, departmentID) values (?, ?)
Hibernate: insert into department (departmentName) values (?)
Hibernate: update employee set employeeName=?, departmentID=? where employeeID=?
如果我們在Employee.hbm.xml映射文件中,給外鍵添加一個非空約束,即:
[html]
<many-to-one class="Department" name="department" column="departmentID" not-null="true"/>
上面的代碼就會報空異常:org.hibernate.PropertyValueException: not-null property references a null or transient value。
如果我們不想改變上面代碼,又要能夠執行。即先持久化從表。對於這種情況我們可以設置級聯:cascade="all".即
[html]
<many-to-one class="Department" name="department" column="departmentID" not-null="true" cascade="all"/>。
通過指定瞭cascade="all"。這就意味著系統將先自動級聯插入主表記錄。
所以在所有基於外鍵約束的關聯關系中,我們必須牢記:要麼總是先持久化主表對應的實體,要麼設置級聯操作。否則當Hibernate試圖插入從表記錄時,如果發現該從表參照的主表記錄不存在,一定會拋出異常。
2、有連接表的N-1關聯
對於絕大部分的單向N-1關聯而言,使用基於外鍵的關聯映射就可以瞭。但是如果需要使用連接表來映射單向N-1關聯,則需要使用<join…/>元素,該元素用於強制將一個類的屬性映射到多張表中。
使用<join…/>元素映射到連接表時,需要外鍵關聯,應在配置文件中增加<key…/>子元素來映射外鍵,並為<join…/>元素增加<many-to-one…/>子元素來映射N-1的關聯實體。如下:
[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="employee_department">
<!– 映射連接表中參照本表主鍵的外鍵列 –>
<key column="employeeID" />
<!– 映射連接表參照關聯實體的外鍵列 –>
<many-to-one name="department" class="Department" column="departmentID" />
</join>
</class>
</hibernate-mapping>
作者:chenssy