由於關系數據模型不允許一個表的外鍵同時參照兩個表的主鍵,因此無法對TABLE_D表的A_ID字段定義外鍵參照約束,而應該通過其他方式,如觸發器,來保證A_ID字段的參照完整性。由於TABLE_D表的A_ID字段既可能參照TABLE_B表的ID主鍵,也可能參照TABLE_C表的ID主鍵,要求TABLE_B表和TALBE_C表的ID主鍵具有相同的SQL類型。
在ClassD.hbm.xml文件中,用元素來映射ClassD的a屬性:
<any name="a"
meta-type="string"
id-type="long"
cascade="save-update">
<meta-value value="B" class="ClassB" />
<meta-value value="C" class="ClassC" />
<column name="A_TYPE" />
<column name="A_ID" />
</any>
元素的meta-type屬性指定TABLE_D中A_TYPE字段的類型,id-type屬性指定TABLE_D中A_ID字段的類型,子元素設定A_TYPE字段的可選值。在本例中,如果A_TYPE字段取值為"B",表示為ClassB的對象,A_ID字段參照TABLE_B表中的ID主鍵;如果A_TYPE字段取值為"C",表示為ClassC的對象,A_ID字段參照TABLE_C表中的ID主鍵。子元素指定TABLE_D表中的A_TYPE字段和A_ID字段,必須先指定A_TYPE字段,再指定A_ID字段。
小結
本章介紹了映射繼承關系的三種方式:
繼承關系樹的每個具體類對應一個表:在具體類對應的表中,不僅包含和具體類的屬性對應的字段,還包含和具體類的父類的屬性對應的字段。這種映射方式不支持多態關聯和多態查詢。
繼承關系樹的根類對應一個表:在根類對應的表中,不僅包含和根類的屬性對應的字段,還包含和所有子類的屬性對應的字段。
這種映射方式支持多態關聯和多態查詢,並且能獲得最佳查詢性能,缺點是需要對關系數據模型進行非常規設計,在數據庫表中加入額外的區分各個子類的字段,此外,不能為所有子類的屬性對應的字段定義not null約束。
繼承關系樹的每個類對應一個表:在每個類對應的表中只需包含和這個類本身的屬性對應的字段,子類對應的表參照父類對應的表。
這種映射方式支持多態關聯和多態查詢,而且符合關系數據模型的常規設計規則,缺點是它的查詢性能不如第二種映射方式。在這種映射方式下,必須通過表的內連接或左外連接來實現多態查詢和多態關聯。
在默認情況下,對於簡單的繼承關系樹可以采用根類對應一個表的映射方式。如果必須保證關系數據模型的數據完整性,可以采用每個類對應一個表的映射方式。對於復雜的繼承關系樹,可以將它分解為幾棵子樹,對每棵子樹采用不同的映射方式。
當然,在設計域模型時,應該盡量避免設計過分復雜的繼承關系,這不僅會增加把域模型映射到關系數據模型的難度,而且也會增加在Java程序代碼中操縱持久化對象的復雜度。
對於不同的映射方式,必須創建不同的關系數據模型和映射文件,但是域模型是一樣的,域模型中的持久化類的實現也都一樣。
只要具備Java編程基礎知識,就能創建具有繼承關系的持久化類,因此本章沒有詳細介紹這些持久化類的創建過程,在此僅提醒一點,子類的完整構造方法不僅負責初始化子類本身的屬性,還應該負責初始化從父類中繼承的屬性,例如以下是HourlyEmployee類的構造方法:
public class HourlyEmployee extends Employee{
private double rate;
/** 完整構造方法*/
public HourlyEmployee(String name, double rate,Company company) {
super(name,company);
this.rate=rate;
}
/** 默認構造方法*/
public HourlyEmployee() {}
……
}
Hibernate只會訪問持久化類的默認構造方法,永遠不會訪問其他形式的構造方法。提供以上形式的完整構造方法,主要是為Java應用的編程提供方便。