每具體類一表 是最直觀但功能最受限的策略:每個具體子類獨立映射一張表,包含所有繼承字段,父類若為抽象則不建表。本文通過完整代碼演示其操作細節,揭示「無多態查詢/關聯」的核心缺陷。
HOURLY_EMPLOYEES (HE) — ID, NAME, RATE, COMPANY_IDSALARIED_EMPLOYEES (SE) — ID, NAME, SALARY, COMPANY_IDCOMPANIES — 公司表無父類 EMPLOYEES 表。ID 由各表自增或共享序列生成。
.hbm.xml<!-- HourlyEmployee.hbm.xml -->
<hibernate-mapping>
<class name="mypack.HourlyEmployee" table="HOURLY_EMPLOYEES">
<id name="id" column="ID"><generator class="increment"/></id>
<property name="name" column="NAME"/>
<property name="rate" column="RATE" type="double"/>
<many-to-one name="company" column="COMPANY_ID" class="mypack.Company"/>
</class>
</hibernate-mapping>
<!-- SalariedEmployee.hbm.xml 類似 -->
初始化需註冊所有具體類:
config.addClass(Company.class)
.addClass(HourlyEmployee.class)
.addClass(SalariedEmployee.class);
public List findAllEmployees() {
List results = new ArrayList();
// 必須分別查詢再合併
results.addAll(session.find("from HourlyEmployee"));
results.addAll(session.find("from SalariedEmployee"));
return results;
}
生成兩條獨立 SQL:SELECT * FROM HOURLY_EMPLOYEES; / SELECT * FROM SALARIED_EMPLOYEES;
❌ 無法用 from Employee 進行多態查詢(Employee 為抽象類會拋異常,具體類只查自身表)。
Company.employees 集合無法自動包含子類實例。業務代碼必須手動查詢並填充:
Company c = (Company) session.load(Company.class, id);
List hourly = session.find("from HourlyEmployee h where h.company.id=?", id);
List salaried = session.find("from SalariedEmployee s where s.company.id=?", id);
c.getEmployees().addAll(hourly);
c.getEmployees().addAll(salaried);
❌ 破壞透明持久化,增加業務代碼複雜度。
session.save(new HourlyEmployee("Mary", 300, company));
SQL:INSERT INTO HOURLY_EMPLOYEES (ID, NAME, RATE, COMPANY_ID) VALUES (3, 'Mary', 300, 1);
✅ 單表操作簡單高效,但主鍵生成策略需跨表協調(如共享序列)。
| 特性 | 支援度 | 影響 |
|---|---|---|
| 多態查詢 (from Parent) | ❌ 不支援 | 需手動 UNION/合併集合 |
| 多態關聯 (One-to-Many) | ❌ 不支援 | 需手動查詢填充集合 |
| 主鍵跨表唯一 | ⚠️ 需額外機制 | 推薦共享序列或 UUID |
| Schema 修改 | ⚠️ 需修改所有子表 | 維護成本高 |
結論:除非極特殊理由,強烈不建議 在新項目使用此策略。其「看似簡單」實則將複雜度轉移給了業務代碼,違背 ORM 透明持久化初衷。