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]