Hibernate讀書筆記—繼承映射 – JAVA編程語言程序開發技術文章

 對於面向對象的程序設計語言,繼承、多態是兩個最基本的概念。Hibernate的繼承映射可以理解兩個持久化類之間的繼承關系。
          Hibernate支持幾種繼承映射策略,不管哪種繼承映射策略,Hibernate的多態查詢都可以很好的運行。
          假設有四個對象:Department、Employee、Skiller、Sales。他們四個對象存在如下關系:

          從上面的關系可知:Employee與Department之間存在N-1的關聯關系。Skiller和Sales都是Employee的子類。下面是這四個類的代碼:
          Department
[java]
public class Department { 
    private Integer id; 
    private String name; 
    private Set<Employee> employees;          //關聯關系 
     
    //省略setter和getter方法 

          Employee
[java] 
public class Employee { 
    private Integer id; 
    private String name; 
    private Department department; 
    //省略setter和getter方法 

         Skiller
[java]
public class Skiller extends Employee { 
    private String skiller; 
    //省略setter和getter方法 

          Sales
[java] 
public class Sales extends Employee { 
    private int sell; 
    //省略setter和getter方法 

 
          一、采用subclass元素的繼承映射
          在這種繼承映射策略下,整個繼承樹的所有實例都將保存在同一張表中。因為是將父類、子類的實例全部保存在同一張表中,所以需要在該表中添加列,通過該列可以區分每行記錄到底是屬於哪個類的實例—這個列被稱之為辨別者。
          在這種繼承映射策略下,我們需要使用<subclass…/>元素來映射子類的持久化類,使用<discrimainator…/>元素來映射辨別者,同時還需要給<subclass…/>元素增加discriminator-value屬性來指定每個類的辨別者的值。
          映射文件如下:
[html] 
<hibernate-mapping package="com.hibernate.domain"> 
    <class name="Employee" table="employee"> 
        <id name="id" column="employeeID"> 
            <generator class="hilo" /> 
        </id> 
         <!– 映射辨別者 –> 
        <discriminator column="type" type="string" /> 
         
        <property name="name" column="employeeName" /> 
        <!– 用於映射N-1關聯實體,指定關聯實體類為 :Department,指定外鍵為:departmentID–> 
        <many-to-one name="department" column="departmentID" />   
         
        <!– 使用subclass元素映射Employee的子類Skiller –>  
        <subclass name="Skiller" discriminator-value="skiller"> 
            <property name="skiller" /> 
        </subclass> 
         
        <!– 使用subclass元素映射Employee的子類Sales –>  
        <subclass name="Sales" discriminator-value="sales"> 
            <property name="sell" /> 
        </subclass> 
    </class> 
</hibernate-mapping> 
          在這裡我們隻需要給父類進行映射就可以瞭。在這個配置文件中,指定瞭一個辨別者列:type,該列其本省是沒有任何意義的,隻是起到一個區分每條記錄時對應哪個持久化類的作用。其中Skiller類的辨別者列type的值為skiller,Sales類的辨別者列type的值為sales。通過下面的程序段來進行操作。
[html] 
static void add(){ 
    Session session = HibernateUtil.getSession(); 
    Transaction tx = session.beginTransaction(); 
     
    Department department = new Department(); 
    department.setName("department name1"); 
     
    Employee emp1 = new Employee(); 
    emp1.setName("employy name1"); 
    emp1.setId(1); 
     
    Skiller emp2 = new Skiller(); 
    emp2.setSkiller("電工"); 
    emp2.setName("employee name2"); 
    emp2.setDepartment(department);        //建立對象之間的關聯關系 
     
    Sales emp3= new Sales(); 
    emp3.setSell(50); 
    emp3.setName("employee name3"); 
    emp3.setDepartment(department);        //建立對象之間的關聯關系 
     
    session.save(department); 
    session.save(emp1); 
    session.save(emp2); 
    session.save(emp3); 
     
    tx.commit(); 
    session.close(); 
     

          上面的程序段,hibernate將會產生如下的幾條SQL語句:
[sql] view plaincopyprint?
Hibernate: insert into department (departmentName) values (?) 
 
Hibernate: insert into employee (employeeName, departmentID, type) values (?, ?, 'com.hibernate.domain.Employee') 
 
Hibernate: insert into employee (employeeName, departmentID, skiller, type) values (?, ?, ?, 'skiller') 
 
Hibernate: insert into employee (employeeName, departmentID, sell, type) values (?, ?, ?, 'sales') 

          在第二條sql語句,type的值為com.hibernate.domain.Employee。對於這個值可以理解為空。在第三條sql語句中,type的值為skiller,第四天sql語句中type值為sales。同時要註意第三條sql語句中的sell列是沒有值的,第四條的skiller列同樣也沒有值。所以在這裡一定要註意:使用subclass繼承映射策略時,其子類中增加的屬性映射的字段是一定不能有非空約束的。
          表結構如下:

          通過下面的程序段,來進行查詢操作:
[java]
static void query(int empid){ 
 Session session = HibernateUtil.getSession(); 
 Employee emp = (Employee) session.get(Employee.class, empid); 

          得到如下SQL語句:
[sql] 
Hibernate: select employee0_.employeeID as employeeID2_0_, employee0_.employeeName as employee3_2_0_, employee0_.departmentID as departme4_2_0_, employee0_.skiller as skiller2_0_, employee0_.sell as sell2_0_, employee0_.type as type2_0_  
 
from employee employee0_ where employee0_.employeeID=? 

          從上面的sql語句可以看到它是從一張表中獲取所有的記錄。這正是這種繼承策略的一個非常大的好處:在這種映射策略下,整棵繼承樹的所有數據都保存在一張表中,因此不管進行怎樣的查詢,不管查詢繼承樹中的那一層的實體,底層數據庫都隻需要在一張表中查詢即可,非常方便、高效。
          我們在這裡給上面的查詢代碼增加一句:System.out.println(emp.getClass());
          依次給empid賦值為:1、2得到的結果如下:
          1、class com.hibernate.domain.Employee
          2、class com.hibernate.domain.Skiller
          有上面的輸出結果可知:hibernate能夠非常好處理多態查詢。
 
          一、采用joined-subclass元素的繼承映射
          采用這種策略時,父類實例保存在父類表裡,而子類實例則有父類表和子類表共同存儲。在這種策略下,是將父類與子類共有的屬性保存在父類表中,而子類特有的屬性,則保存在子類表中,就是說,父類一張表,子類一張表,同時子類表中需要增加父類表的外鍵。
          采用joined-subclass來映射這種策略,並且需要為每個子類使用<key…/>元素映射共有主鍵–這個主鍵類還將參照父表的主鍵列。
          映射文件如下:
[html] 
<hibernate-mapping package="com.hibernate.domain"> 
    <class name="Employee" table="employee"> 
        <id name="id" column="employeeID"> 
            <generator class="hilo" /> 
        </id> 
 
        <property name="name" column="employeeName" /> 
        <!– 用於映射N-1關聯實體,指定關聯實體類為 :Department,指定外鍵為:departmentID–> 
        <many-to-one name="department" column="departmentID" />   
 
        <!– 使用join-class元素映射Employee類的Skill而子類 –> 
        <joined-subclass name="Skiller"> 
            <!– 必須使用key元素映射父子類的共有主鍵 –> 
            <key column="employee_id" /> 
            <property name="skiller" /> 
        </joined-subclass> 
         
        <!– 使用join-class元素映射Employee類的Sales而子類 –> 
        <joined-subclass name="Sales"> 
            <!– 必須使用key元素映射父子類的共有主鍵 –> 
            <key column="employee_id" /> 
            <property name="sell" /> 
        </joined-subclass> 
    </class> 
</hibernate-mapping> 

          通過上面的增加代碼,執行添加操作。得到如下結果:

     
          SQL語句如下:
[sql] 
Hibernate: insert into department (departmentName) values (?) 
 
Hibernate: insert into employee (employeeName, departmentID) values (?, ?) 
 
Hibernate: insert into employee (employeeName, departmentID) values (?, ?) 
 
Hibernate: insert into Skiller (skiller, employee_id) values (?, ?) 
 
Hibernate: insert into employee (employeeName, departmentID) values (?, ?) 
 
Hibernate: insert into Sales (sell, employee_id) values (?, ?) 

          從上面的SQL語句中也可以看出hibernate對與這種繼承映射策略的處理。父類一張表,子類一張表同時子類引用父類外鍵建立關聯關系。
          對與這樣繼承映射策略的查詢,hibernate采用表連接方式來獲取子類表的信息:
[sql] 
Hibernate: select employee0_.employeeID as employeeID2_0_, employee0_.employeeName as employee2_2_0_, employee0_.departmentID as departme3_2_0_, employee0_1_.skiller as skiller3_0_, employee0_2_.sell as sell4_0_, case  
 
when employee0_1_.employee_id is not null then 1  
 
when employee0_2_.employee_id is not null then 2  
 
when employee0_.employeeID is not null then 0 end as clazz_0_  
 
from employee employee0_ left  
 
outer join Skiller employee0_1_  
 
on employee0_.employeeID=employee0_1_.employee_id  
 
left outer join Sales employee0_2_  
 
on employee0_.employeeID=employee0_2_.employee_id  
 
where employee0_.employeeID=? 

          所以當使用joined-subclass繼承映射策略,當程序查詢子類實例時,需要跨越多個字表查詢,其復雜度取決於該子類有多少層父類。這樣勢必會對查詢的性能有些影響。
 
          一、采用union-subclass元素的繼承映射
          采用這樣繼承映射策略,父類實例的數據保存在父表中,子類實例的數據僅僅隻保存在字表中,沒有在附表中有任何記錄。在這種繼承映射策略下,子表的字段會比父表的字段多。
          同時在這種映射策略,既不需要使用辨別者,也不需要使用<key…/>元素來映射共有主鍵。
          映射文件如下:
[html]
<hibernate-mapping package="com.hibernate.domain"> 
    <class name="Employee" table="employee"> 
        <id name="id" column="employeeID"> 
            <generator class="hilo" /> 
        </id> 
 
        <property name="name" column="employeeName" /> 
        <!– 用於映射N-1關聯實體,指定關聯實體類為 :Department,指定外鍵為:departmentID–> 
        <many-to-one name="department" column="departmentID" />   
 
        <!– 使用union-subclass元素映射Employee類的子類:Skiller –> 
        <union-subclass name="Skiller" table="skiller"> 
            <property name="skiller" /> 
        </union-subclass> 
         
        <!– 使用union-subclass元素映射Employee類的子類:Sales –> 
        <union-subclass name="Sales" table="sales"> 
            <property name="sell" /> 
        </union-subclass> 
    </class> 
</hibernate-mapping>   www.aiwalls.com

          註:在這種映射策略下,映射持久化類是不能使用identity主鍵生成策略。
          通過添加操作得到如下的兩個子類表:
 

          采用這中映射策略,底層數據庫的數據看起來更加符合正常情況下的數據庫設計,不同實體保存在不同的數據表中。

 作者:chenssy
 

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。