在領域模型中,繼承關係是核心概念之一。以 Employee 抽象類別為例,衍生出 HourlyEmployee(時薪員工)與 SalariedEmployee(月薪員工)兩個具體子類別,構成繼承樹。資料庫不支援繼承,Hibernate 提供三種映射策略將物件繼承轉換為關聯式資料模型。
多態讓 Employee 變數可引用兩種子類別實例:
List employees = businessService.findAllEmployees();
for (Employee e : employees) {
if (e instanceof HourlyEmployee)
System.out.println(e.getName() + " " + ((HourlyEmployee)e).getRate());
else
System.out.println(e.getName() + " " + ((SalariedEmployee)e).getSalary());
}
findAllEmployees() 回傳包含兩種子類別的集合,即多態查詢。Company 到 Employee 為多態關聯(集合含子類別實例),反向則非多態。
| 策略 | 資料表結構 | 支援多態 | 優點 | 缺點 |
|---|---|---|---|---|
| 每個具體類別一張表 (Table per Concrete Class) |
HourlyEmployee、SalariedEmployee 各自獨立表,包含所有繼承欄位 | 不支援 | 查詢單一類別效能最好,無 JOIN | 無法多態查詢,結構重複,修改父類欄位需改所有表 |
| 根類別一張表 (Table per Class Hierarchy) |
單一 Employee 表,加 discriminator 欄位區分子類型 |
支援 | 多態查詢快(無 JOIN),結構簡單 | 子類別特有欄位須設 NULL,資料完整性較難維護,表膨脹 |
| 每個類別一張表 (Table per Subclass) |
Employee 表 + HourlyEmployee/SalariedEmployee 表,透過 FK 關聯 | 支援 | 資料正規化,完整性佳,欄位不重複 | 多態查詢需 JOIN,效能較差;結構較複雜 |
根類別表(單表策略):
<class name="Employee" table="EMPLOYEE">
<id name="id" column="EMP_ID"><generator class="native"/></id>
<discriminator column="EMP_TYPE" type="string"/>
<property name="name" column="NAME"/>
<subclass name="HourlyEmployee" discriminator-value="HOURLY">
<property name="rate" column="HOURLY_RATE"/>
</subclass>
<subclass name="SalariedEmployee" discriminator-value="SALARIED">
<property name="salary" column="MONTHLY_SALARY"/>
</subclass>
</class>
子類別表(聯結策略):
<class name="Employee" table="EMPLOYEE">
<id name="id" column="EMP_ID"><generator class="native"/></id>
<property name="name" column="NAME"/>
<joined-subclass name="HourlyEmployee" table="HOURLY_EMP">
<key column="EMP_ID"/>
<property name="rate" column="HOURLY_RATE"/>
</joined-subclass>
<joined-subclass name="SalariedEmployee" table="SALARIED_EMP">
<key column="EMP_ID"/>
<property name="salary" column="MONTHLY_SALARY"/>
</joined-subclass>
</class>
無銀彈,三種策略各有取捨。實務上常混合使用:核心領域模型採用子類別表保證正規化;高頻查詢、唯讀報表模型可考慮根類別表或具體類別表換取效能。理解多態查詢與關聯的語意差異,是選型關鍵。