在先前的文章中,我們探討了使用單表策略將一張邏輯表拆分為多張表的做法。然而,這種方式容易產生「空巢欄位」問題——即邏輯上屬於不同子表的欄位在物理表中大量留空。為了解決此問題,本文將示範如何使用 EJB3 的 @Inheritance(strategy = InheritanceType.JOINED) 策略,將 t_accounts 實體拆分為多張獨立的物理資料表。
首先建立一張主表 t_myaccounts,僅保留共同欄位:account_id、balance、account_type。接著建立兩張子表:
account_id(外鍵)與 overdraft_limit(透額限額)。account_id(外鍵)與 interest_rate(利率)。兩張子表的 account_id 均參考主表的主鍵,形成一對一的關聯關係。
@Entity
@Table(name = "t_myaccounts")
@Inheritance(strategy = InheritanceType.JOINED)
public class Account {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "account_id")
protected String id;
protected float balance;
@Column(name = "account_type")
protected String type;
// Getter/Setter 略...
}
關鍵在於 @Inheritance(strategy = InheritanceType.JOINED),告知 JPA 提供者採用「連接策略」:父類別對應主表,子類別各自對應獨立子表,透過主鍵關聯。
@Entity
@Table(name = "t_checkingaccount")
@PrimaryKeyJoinColumn(name = "account_id")
public class MyCheckingAccount extends Account {
private double overdraftLimit;
public MyCheckingAccount() {
setType("C"); // 預設類型為活期
}
@Column(name = "overdraft_limit")
public double getOverdraftLimit() { return overdraftLimit; }
public void setOverdraftLimit(double overdraftLimit) { this.overdraftLimit = overdraftLimit; }
}
@Entity
@Table(name = "t_savingsaccount")
@PrimaryKeyJoinColumn(name = "account_id")
public class MySavingsAccount extends Account {
private double interestRate;
public MySavingsAccount() {
setType("S"); // 預設類型為儲蓄
}
@Column(name = "interest_rate")
public double getInterestRate() { return interestRate; }
public void setInterestRate(double interestRate) { this.interestRate = interestRate; }
}
子類別使用 @PrimaryKeyJoinColumn 指定共享主鍵欄位名,並透過建構子自動設定判別欄位 account_type 的值。
// 查詢
MyCheckingAccount ca = em.createQuery("from MyCheckingAccount where id=12", MyCheckingAccount.class).getSingleResult();
System.out.println(ca.getBalance());
// 新增活期帳戶
MyCheckingAccount newCa = new MyCheckingAccount();
newCa.setBalance(342);
newCa.setOverdraftLimit(120);
em.persist(newCa);
// 新增儲蓄帳戶
MySavingsAccount newSa = new MySavingsAccount();
newSa.setBalance(200);
newSa.setInterestRate(3.21);
em.persist(newSa);
JOINED 策略將繼承階層映射為正規化的資料表結構,避免了單表策略的空值浪費,適合欄位差異較大的子類別場景。缺點是查詢時需進行 JOIN 操作,效能略低於單表策略。