天天看點

Hibernate 繼承關系的映射

Hibernate 中支援的 3種類型的繼承關系:

1,表與子類之間的獨立的一對一關系

2,每個子類對應一張子表,并與主類共享主表

3,表與類的一對多關系

1,表與子類之間的獨立的一對一關系

這種情況 是最普通的一個類對應一個表,就不在舉例。

2,每個子類對應一張子表 , 并與主類共享主表

實質:通過調用子類操作自己表和父類的表利用xml檔案中joined-subclass節點

具體執行個體:

package extend.hibernate;

public class Titem implements java.io.Serializable {

private int pids;
private String pname;

public Titem() {
}

public Titem(int pids) {
   this.pids = pids;
}

public Titem(int pids, String pname) {
   this.pids = pids;
   this.pname = pname;
}

public int getPids() {
   return this.pids;
}

public void setPids(int pids) {
   this.pids = pids;
}

public String getPname() {
   return this.pname;
}

public void setPname(String pname) {
   this.pname = pname;
}

}
package extend.hibernate;

public class TDvd extends Titem {
private String version;

public String getVersion() {
   return version;
}

public void setVersion(String version) {
   this.version = version;
}

}

           
package extend.hibernate;

public class TBook extends Titem {
private int pages;

public int getPages() {
   return pages;
}

public void setPages(int pages) {
   this.pages = pages;
}

}
           

3,表與類的一對多關系

實質: 是通過資料庫中的一個字段來區分 (在映射檔案種配置,無須在程式種寫出)調用那個類,利用的是映射檔案的discriminator(鑒别器) 和subclass節點。

具體執行個體:

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

<hibernate-mapping>
<class name="one.hibernate.Item" table="ITEM" schema="SALES">
   <id name="itemid" type="java.lang.Integer">
    <column name="ITEMID" precision="22" scale="0" />
    <generator class="assigned" />
   </id>
  <discriminator column="dif" type="java.lang.String" />
   <property name="itemname" type="java.lang.String">
    <column name="ITEMNAME" length="20" />
   </property>
   <subclass name="one.hibernate.TBook" discriminator-value="1">
   <property name="pages" type="java.lang.Integer">
     <column name="PAGES" precision="22" scale="0" />
    </property>
   </subclass>
   <subclass name="one.hibernate.TDvd" discriminator-value="2">
   <property name="version" type="java.lang.String">
     <column name="VERSION" length="20" />
    </property>
   </subclass>
</class>
</hibernate-mapping>
      

類的關系:

package one.hibernate;

public class Item implements java.io.Serializable {
private int itemid;
private String itemname;
public Item() {
}
public Item(int itemid) {
   this.itemid = itemid;

}
public Item(int itemid, String itemname) {
   this.itemid = itemid;

   this.itemname = itemname;

}

public int getItemid() {
   return this.itemid;
}

public void setItemid(int itemid) {
   this.itemid = itemid;
}

public String getItemname() {
   return this.itemname;
}

public void setItemname(String itemname) {
   this.itemname = itemname;
}

}
           
package one.hibernate;

public class TBook extends Item {
private int pages;

public int getPages() {
   return pages;
}

public void setPages(int pages) {
   this.pages = pages;
}

}
           
package one.hibernate;

public class TDvd extends Item {
private String version;

public String getVersion() {
   return version;
}

public void setVersion(String version) {
   this.version = version;
}

}
           

--------------------------------------------------------------------------------------------------------------------------------

Hibernate 繼承關系的映射

在Java或.Net類與類之間存在關聯、聚集和繼承關系。一般來說:

關聯關系:采用“一對多或一對一”的映射即可;

聚集關系:采用“集合映射”,即映射Set,Bag,List,Map

繼承關系:

因為關系資料庫的表之間不存在繼承關系,是以Hibernate提供了以下三種對繼承關系映射的方法,即在繼承關系樹中:

(1)、每個具體類(非抽象類)對應一個表:此方式中關系資料模型完全不支援對象的繼承關系。

(2)、基類(根類)對應一個表:此方式中對關系資料模型進行非正常設計,在資料庫表中加入額外的區分子類的字段,進而使關系資料模型可以支援繼承關系。

(3)、每個類對應一個表:此方式中在關系資料模型中用外鍵關系來表示繼承關系。

這三種映射方式都各自有利有弊,需要根據具體情況來選擇使用。分别說明一下:

為了說明友善,引用一個比較經典的簡單例子

Employee類,它為抽象類,有兩個直接子類

HourlyEmployee類,Employee的子類,繼承父類的name屬性

SalarilyEmployee類,Employee的子類,繼承父類的name屬性

Company類,它有一個employees集合屬性

繼承關系中每個具體類對應一個表

這是最簡單的映射方式。基類Employee沒用映射,隻需要為兩個子類提供映射關系即可。看一下映射檔案:

<hibernate-mapping package="com.wjb.mypack">

    <!--Company的映射-->

    <class name="Company" table="COMPANIES">

        <id name="id" type="long" column="ID">

            <generator class="native"/>

        </id>

        <property name="name" type="string" column="NAME"/>

    </class>

    <!--HourlyEmployee的映射-->

    <class name="HouredEmployee" table="HOURLY_EMPLOYEES">

        <id name="id" type="long" column="ID">

            <generator class="native"/>

        </id>

        <!--映射從父類繼承的name屬性-->

        <property name="name" type="string" column="NAME"/>

        <property name="rate" type="double" column="RATE"/>

        <many-to-one name="company" column="COMPANY_ID" class="Company"/>

    </class>

    <!--SalarilyEmployee的映射-->

    <class name="SalarilyEmployee" table="SALARILY_EMPLOYEES">

        <id name="id" type="long" column="ID">

            <generator class="native"/>

        </id>

        <!--映射從父類繼承的name屬性-->

        <property name="name" type="string" column="NAME"/>

        <property name="salary" type="double" column="SALARY"/>

        <many-to-one name="company" column="COMPANY_ID" class="Company"/>

    </class>

    <!--注:Employee類沒有映射,在DB中不存在對應的表-->

</hibernate-mapping>

可以看出,在這種映射方式中,每個子類除了映射自己的屬性外,還需要映射從父類繼承來下的屬性,這是該映射方式的一個特點。

基類(根類)對應一個表

這種映射方式隻需為基類Employee建立一個表即可。在表中不僅提供與Employee所有屬性的字段,還要提供與所有子類屬性對應的字段,此外還需要一個字段用于區分子類的具體類型。此時的映射檔案為:

<hibernate-mapping package="com.wjb.mypack">

    <!--Company的映射-->

    <class name="Company" table="COMPANIES">

        <id name="id" type="long" column="ID">

            <generator class="native"/>

        </id>

        <property name="name" type="string" column="NAME"/>

    </class>

    <!--Employee以及子類的映射-->

    <class name="Employee" table="EMPLOYEES">

        <id name="id" type="long" column="ID">

            <generator class="native"/>

        </id>

        <!--用于區分子類類型的字段-->

        <discriminator type="string" column="EMPLOYEE_TYPE">

        <property name="name" type="string" column="NAME"/>

        <many-to-one name="company" column="COMPANY_ID" class="Company"/>

        <!--子類HourlyEmployee的映射-->

        <subclass name="HourlyEmployee" discriminator-value="HE">

            <property name="rate" column="RATE" type="double"/>

        </subclass>

        <!--子類SalarilyEmployee的映射-->

        <subclass name="SalarilyEmployee" discriminator-value="SE">

            <property name="salary" column="SALARY" type="double"/>

        </subclass>

    </class>

    <!--注:HourlyEmployee類沒有單獨的映射,在DB中不存在對應的表-->

    <!--注:SalarilyEmployee類沒有單獨的映射,在DB中不存在對應的表-->

</hibernate-mapping>

可以看出,每個子類沒有單獨的映射,在DB中沒有對應的表存在。而隻有一個記錄所有自身屬性和子類所有屬性的表,在子類為HourlyEmployee的時候,SALARY字段将為NULL,同樣子類為SalarilyEmployee的時候,RATE字段将為NULL。那麼,如果業務邏輯要求SalariedEmployee對象的rate屬性不允許為null,顯然無法在EMPLOYEES表中為SALARY字段定義not null限制,可見這種映射方式無法保證關系資料模型的資料完整性。

每個類對應一個表

這種方式為基類和子類分别建立表,即EMPLOYEES、HE和SE三個表。EMPLOYEES隻包含Employee自己屬性的字段,每個子類的表也同樣隻包含自己類屬性的字段。此外,HE表和SE表都以EMPLOYEE_ID字段作為主鍵,該字段還同時作為外鍵參照EMPLOYEES表。

HourlyEmployee和SalarilyEmployee沒有獨立的映射配置,但是在DB中有相應的表存在,這是其一個特點。

<hibernate-mapping package="com.wjb.mypack">

    <!--Company的映射-->

    <class name="Company" table="COMPANIES">

        <id name="id" type="long" column="ID">

            <generator class="native"/>

        </id>

        <property name="name" type="string" column="NAME"/>

    </class>

    <!--Employee以及子類的映射-->

    <class name="Employee" table="EMPLOYEES">

        <id name="id" type="long" column="ID">

            <generator class="native"/>

        </id>

        <property name="name" type="string" column="NAME"/>

        <many-to-one name="company" column="COMPANY_ID" class="Company"/>

        <!--子類HourlyEmployee的映射-->

        <joined-subclass name="HourlyEmployee" table="HE">

            <key column="EMPLOYEE_ID"/>

            <property name="rate" column="RATE" type="double"/>

        </subclass>

        <!--子類SalarilyEmployee的映射-->

        <joined-subclass name="SalarilyEmployee" table="SE">

            <key column="EMPLOYEE_ID"/>

            <property name="salary" column="SALARY" type="double"/>

        </subclass>

    </class>

    <!--注:HourlyEmployee類沒有單獨的映射,但在DB中有對應的表-->

    <!--注:SalarilyEmployee類沒有單獨的映射,但在DB中有對應的表-->

</hibernate-mapping>

可見,兩個<joined-subclass>元素用于映射兩個子類,<joined-subclass>元素的<key>子元素指定HE表和SE表中既作為主鍵又作為外鍵的EMPLOYEE_ID字段。

三種映射方式的比較和選擇:

為了友善說明為三種方式按順序标号為[1][2][3]。

1、複雜度:

    [1]包含重複字段;

    [2]簡單;

    [3]表較多且之間有外鍵限制;

2、查詢性能:

    [1]若查詢父類需查所有子類表;

    [2]效率高;

    [3]需要表内連接配接或左外連接配接;

3、可維護性:

    [1]若父類屬性變化需要修改所有子類對應的表;

    [2]隻需修改一個表;

    [3]若某個類屬性變化隻修改這個類對應的表;

綜上,選擇時,可以參考以下原則:

1、子類屬性不是非常多時,優先考慮[2],因為其性能最佳。

2、子類屬性非常多,且對性能要求不是很嚴格時,優先考慮[3]