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

Hibernate 進階映射:一對一外鍵關聯雙向映射完整實戰

     編輯:Java 相關知識

延續前文單向映射,本文詳細解析一對一外鍵關聯的雙向映射配置,以 Person(人員)與 IdCard(身分證)為例,展示完整 POJO、映射檔與關鍵註解對照。

領域模型設計

業務規則:每人僅持有一張身分證,每張身分證歸屬一人。資料庫採用外鍵關聯,由 t_person 表持有 id_card_id 外鍵指向 t_idcard 主鍵。

POJO 類別定義

// Person.java
public class Person {
    private int id;
    private String name;
    private IdCard idCard;  // 關聯對象
    
    // Getter/Setter 省略
}

// IdCard.java
public class IdCard {
    private int id;
    private String cardNo;
    private Person person;  // 反向導航
    
    // Getter/Setter 省略
}

映射檔配置詳解

Person.hbm.xml(關係維護端)

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "https://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="org.apple.hibernate">
    <class name="Person" table="t_person">
        <id name="id">
            <generator class="native"/>
        </id>
        <property name="name"/>
        
        <!-- 
            unique="true" 確保一對一語義
            cascade="save-update,delete" 級聯持久化操作
        -->
        <many-to-one name="idCard" 
                     class="IdCard" 
                     column="id_card_id" 
                     unique="true" 
                     cascade="save-update,delete"/>
    </class>
</hibernate-mapping>

IdCard.hbm.xml(被維護端)

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "https://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="org.apple.hibernate">
    <class name="IdCard" table="t_idcard">
        <id name="id">
            <generator class="native"/>
        </id>
        <property name="cardNo"/>
        
        <!-- 
            property-ref="idCard" 指定 Person 端關聯屬性名
            constrained="true" 宣告外鍵約束,確保資料完整性
        -->
        <one-to-one name="person" 
                    class="Person" 
                    property-ref="idCard" 
                    constrained="true"/>
    </class>
</hibernate-mapping>

關鍵配置解析

屬性位置作用必要性
unique="true" <many-to-one> 資料庫層面建立唯一索引,強制一對一 必填
property-ref <one-to-one> 告知 Hibernate 透過 Person.idCard 反向查找,而非主鍵 外鍵模式必填
constrained="true" <one-to-one> 生成 DDL 時添加外鍵約束;刪除 Person 時級聯刪除 IdCard 建議啟用
cascade <many-to-one> 定義持久化操作傳播範圍 視業務而定

JPA 註解對照(Hibernate 5+ 推薦)

// Person.java
@Entity
@Table(name = "t_person")
public class Person {
    @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int id;
    
    private String name;
    
    @OneToOne(cascade = CascadeType.ALL)
    @JoinColumn(name = "id_card_id", unique = true)
    private IdCard idCard;
    // ...
}

// IdCard.java
@Entity
@Table(name = "t_idcard")
public class IdCard {
    @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int id;
    
    private String cardNo;
    
    @OneToOne(mappedBy = "idCard")
    private Person person;
    // ...
}

常見陷阱與最佳實踐

  1. 雙向關聯維護:永遠在關係維護端(Person)設置關聯,被維護端僅提供導航
    // 正確做法
    person.setIdCard(idCard);
    idCard.setPerson(person); // 僅為物件圖一致性,不觸發 SQL
  2. 延遲載入陷阱:one-to-one 預設 fetch=EAGER,易造成 N+1 查詢。建議:
    @OneToOne(fetch = FetchType.LAZY)
    @LazyToOne(LazyToOneOption.NO_PROXY) // 需啟用位元組碼增強
  3. 主鍵共享 vs 外鍵關聯:本文為外鍵模式(兩表各有主鍵)。若採用主鍵共享,IdCard 主鍵同時是外鍵,映射改為 <one-to-one constrained="true"/> 且無 property-ref。

測試驗證代碼

@Test
public void testBidirectionalOneToOne() {
    Session session = sessionFactory.openSession();
    Transaction tx = session.beginTransaction();
    
    IdCard card = new IdCard();
    card.setCardNo("A123456789");
    
    Person person = new Person();
    person.setName("張三");
    person.setIdCard(card);
    card.setPerson(person); // 雙向綁定
    
    session.save(person); // 級聯儲存 card
    
    tx.commit();
    session.close();
    
    // 驗證反向查詢
    Session session2 = sessionFactory.openSession();
    IdCard loadedCard = session2.get(IdCard.class, card.getId());
    assertNotNull(loadedCard.getPerson());
    assertEquals("張三", loadedCard.getPerson().getName());
    session2.close();
}

掌握 property-ref 與 constrained 兩大核心屬性,即可駕馭一對一外鍵雙向映射的各種變體場景。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved