物件導向繼承與關係型資料庫平面結構間存在經典的「阻抗失配」。OpenJPA 提供三種繼承映射策略,透過 @Inheritance(strategy=...) 統一聲明,開發者專注領域模型,框架自動處理持久化與查詢轉換。
| 策略 | 註解值 | 資料庫表現 | 優點 | 缺點 |
|---|---|---|---|---|
| 單表策略 | SINGLE_TABLE |
所有類共用一張表,含所有欄位 + 鑑別欄 DTYPE |
查詢最快(無 JOIN)、結構簡單 | 欄位稀疏(大量 NULL)、違反正規化、繼承層次深時表極寬 |
| 連接策略 | JOINED |
父表含共同欄位,子表含各自欄位 + 主鍵外鍵關聯父表 | 符合正規化、無冗余、支援多態查詢高效 | 查詢需 JOIN、插入需多表操作 |
| 每類一表 | TABLE_PER_CLASS |
每個具體類一張獨立表,含所有繼承欄位,表間無關聯 | 無 JOIN、子表獨立演進 | 多態查詢需 UNION 效能差、主鍵跨表唯一難保證、Schema 維護麻煩 |
領域模型:Animal (父) → Fish (territory)、Dog (sex)。
// Animal.java (父類,聲明繼承策略)
@Entity
@Inheritance(strategy=InheritanceType.JOINED)
public class Animal {
@Id private int id;
private String name;
// 建構子、getter/setter...
}
// Fish.java (子類)
@Entity
public class Fish extends Animal {
private String territory; // 活動水域
// 建構子、getter/setter...
}
// Dog.java (子類)
@Entity
public class Dog extends Animal {
private String sex; // 性別
// 建構子、getter/setter...
}
CREATE TABLE ANIMAL (ID INTEGER PRIMARY KEY, NAME VARCHAR(255));
CREATE TABLE DOG (ID INTEGER PRIMARY KEY, SEX VARCHAR(255));
CREATE TABLE FISH (ID INTEGER PRIMARY KEY, TERRITORY VARCHAR(255));
-- 子表 ID 同時為 PK 與 FK 參照 ANIMAL.ID
代碼與普通實體無異,框架自動分發到多表:
em.persist(new Fish(2, "mermaid", "SEA"));
// 生成 SQL:
// INSERT INTO ANIMAL (ID, NAME) VALUES (2, 'mermaid');
// INSERT INTO FISH (ID, TERRITORY) VALUES (2, 'SEA');
JPQL: SELECT a FROM Animal a
生成 SQL (LEFT JOIN 子表):
SELECT t0.ID, t1.ID, t2.ID, t0.NAME, t1.SEX, t2.TERRITORY
FROM ANIMAL t0
LEFT OUTER JOIN DOG t1 ON t0.ID = t1.ID
LEFT OUTER JOIN FISH t2 ON t0.ID = t2.ID
結果自動映射為 Animal、Dog、Fish 實例 (依 DTYPE 或表存在性判斷)。
JPQL: SELECT f FROM Fish f → INNER JOIN ANIMAL 只查對應子表記錄。
SINGLE_TABLE (犧牲空間換查詢速度)。JOINED (最佳平衡,推薦預設)。TABLE_PER_CLASS (慎用,JPA 2.0 才標準化支援)。同一應用中可混合使用不同策略 (針對不同繼承樹)。
OpenJPA 以 @Inheritance 註解優雅解決繼承映射難題。JOINED 策略兼顧正規化、多態查詢與 Schema 清晰度,是最佳實踐首選。開發者無需編寫 SQL,框架自動處理多表插入、LEFT/INNER JOIN 查詢與結果多態映射,真正實現領域模型與持久化邏輯分離。