Hibernate讀書筆記—–Hibernate的關聯映射之N-1關聯映射 – JAVA編程語言程序開發技術文章

  我們所生活的世界很少有事物是孤立存在的,每個事物必然會存在著與它相關聯的事物。在面向對象設計當中,關聯關系是非常重要的。關聯關系一般可以分為以下兩種:
          單向關系:隻需單向訪問關聯端
          雙向關系:關聯的兩端都可以互相訪問
          單向關系可分為: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

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *