| 維度 | 繼承映射 | Type 字段區分 |
|---|---|---|
| 面向對象純度 | 高(符合 LSP) | 低(違反封裝,屬於貧血模型) |
| 字段冗余 | 無(各子類獨立表) | 有(所有屬性在一表,大量 NULL) |
| 角色轉換 | 困難(需刪除舊記錄、插入新記錄) | 簡單(UPDATE type 字段即可) |
| 自增 ID 衝突 | 存在(刪插導致 ID 變化) | 無(同一記錄 ID 不變) |
| 查詢性能 | 需 JOIN/UNION | 單表查詢,極快 |
| 維護複雜度 | 高(Schema 變更需遷移多表) | 低(單表加字段) |
繼承映射下,User → Admin 本質是刪除 User 記錄、插入 Admin 記錄。若使用自增 ID(Identity/Sequence),新記錄 ID 必然改變,導致:
// 方案 A:使用 UUID/分配器生成 ID,刪插時保持 ID 不變
@Entity
@Inheritance(strategy = InheritanceStrategy.JOINED)
public abstract class User {
@Id
@GeneratedValue(generator = "uuid2")
@GenericGenerator(name = "uuid2", strategy = "uuid2")
private String id; // 業務主鍵,轉換時保持不變
// ...
}
// 方案 B:組合模式替代繼承(推薦)
@Entity
public class User {
@Id @GeneratedValue
private Long id;
@ManyToOne
private Role role; // 動態關聯角色,支持多角色、隨時變更
// 用戶通用屬性...
}
@Entity
public class Role {
@Id @GeneratedValue
private Long id;
private String code; // "ADMIN", "USER", "MANAGER"
private String name;
// 權限集合...
}
InheritanceType.SINGLE_TABLE + @DiscriminatorColumn,單表繼承避免 JOIN,配合 UUID 主鍵解決轉換 ID 不變問題@Enumerated(EnumType.STRING)「純粹的面向對象」在持久層往往需要妥協。組合優於繼承是領域驅動設計(DDD)的核心原則之一,角色/權限系統尤為適用。繼承映射適合「結構固定、無轉換需求」的場景(如:貨幣類型、地址類型)。