首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

EJB 3 開發指南(7):一對一關聯映射 - 外鍵與共享主鍵雙模式

     編輯:Java 相關知識

一對一關聯在 EJB 3 中支援兩種映射模式:外鍵模式(主表持有從表 FK)與 共享主鍵模式(從表 PK 同時為 FK)。本文以 Customer(客戶)與 Referee(推薦人)為例,完整示範雙向導航、外鍵自訂、共享主鍵配置與持久化差異。

一、資料表結構

外鍵模式

t_customers

id (PK) | name | referee_id (FK → t_referees.id)

t_customers 表結構

t_referees

id (PK) | name

t_referees 表結構

二、外鍵模式映射 (預設)

Customer 類別(關聯維護端)

@Entity
@Table(name = "t_customers")
public class Customer {
    private int id;
    private String name;
    private Referee referee;

    @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
    public int getId() { return id; }
    // ... name getter/setter

    @OneToOne  // 預設外鍵名:referee_id (屬性名_id)
    public Referee getReferee() { return referee; }
    public void setReferee(Referee referee) { this.referee = referee; }
}

Referee 類別(被關聯端)

@Entity
@Table(name = "t_referees")
public class Referee {
    private int id;
    private String name;
    private Customer customer;

    @OneToOne(mappedBy = "referee")  // 指向維護端屬性名
    public Customer getCustomer() { return customer; }
    public void setCustomer(Customer customer) { this.customer = customer; }

    @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
    public int getId() { return id; }
    // ... name getter/setter
}

自訂外鍵名稱

若外鍵欄位非預設 referee_id 而是 referee1_id,兩種方式:

方式 1:修改屬性名(影響物件模型)

// Customer
private Referee referee1;
@OneToOne
public Referee getReferee1() { return referee1; }

// Referee
@OneToOne(mappedBy = "referee1")
public Customer getCustomer() { ... }

方式 2:@JoinColumn 覆蓋 (推薦,不影響物件模型)

@OneToOne
@JoinColumn(name = "referee1_id")
public Referee getReferee() { return referee; }

三、共享主鍵模式

從表 t_referees.id 值依賴主表 t_customers.id,不再需要 referee_id 外鍵欄位。t_referees.id 移除自增。

Customer 類別修改

@OneToOne(cascade = CascadeType.ALL)  // 級聯持久化 Referee
@PrimaryKeyJoinColumn  // 共享主鍵
public Referee getReferee() { return referee; }

Referee 類別修改

@OneToOne
@PrimaryKeyJoinColumn
public Customer getCustomer() { return customer; }

@Id
@GeneratedValue(generator = "pkGenerator")
@GenericGenerator(
    name = "pkGenerator",
    strategy = "foreign",
    parameters = @Parameter(name = "property", value = "customer")
)
public int getId() { return id; }

關鍵註解解析

註解作用
@PrimaryKeyJoinColumn雙方皆標記,聲明共享主鍵關聯
@GenericGenerator(strategy="foreign")Hibernate 擴充,指定 ID 來源為關聯實體的屬性
@Parameter(name="property", value="customer")指向 Referee 端的關聯屬性名
cascade=ALLCustomer 持久化時自動持久化 Referee

四、持久化測試與差異

外鍵模式


// 可獨立持久化 Referee
em.persist(referee);
customer.setReferee(referee);
em.persist(customer);

共享主鍵模式


// 必須先設定關聯,再持久化 Customer (Referee 無法獨立持久化)
Customer customer = new Customer();
customer.setName("微軟");
Referee referee = new Referee();
referee.setName("趙軍");
customer.setReferee(referee);
em.persist(customer);  // 容器自動持久化 Referee,ID 同步為 Customer ID

五、查詢測試

// 外鍵模式:可獨立查詢 Referee
System.out.println(em.find(Referee.class, 1).getCustomer().getName());

// 共享主鍵模式:PK 相同
Customer c = em.find(Customer.class, 1);
Referee r = em.find(Referee.class, 1);  // 同一 ID
assert c.getId() == r.getId();

六、模式選擇指南

場景推薦模式理由
從表實體獨立存在、可能被多方引用外鍵模式獨立身分、可共享、支援獨立查詢
從表實體強依賴主表、生命週期綁定共享主鍵資料庫層級強制 1:1、節省 FK 欄位、查詢效能略優
既有資料表已有 FK 欄位外鍵模式零遷移成本
新系統設計、嚴格 1:1 組合關係共享主鍵領域模型更純粹、避免 FK 管理

七、常見錯誤

  • 共享主鍵忘記移除 ID 自增:資料庫建表腳本中 t_referees.id 必須移除 AUTO_INCREMENT/IDENTITY
  • 雙向關聯缺少 mappedBy:會產生兩個單向關聯,建立兩個外鍵或關聯表
  • cascade 設錯:共享主鍵模式 Customer 端必須 cascade=ALL,否則 Referee 不會被持久化
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved