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

Hibernate 單向一對多映射實戰:Set 集合、外鍵與 inverse 陷阱完整指南

     編輯:Java 相關知識

單向一對多:只在「一方」 (User) 維護關聯集合,「多方」 (Card) 無導航屬性。優點:簡單、避免雙向同步複雜度。缺點:無法從 Card 導航到 User。

領域模型

public class User {
    private String id;
    private String name;
    private Set<Card> cards = new HashSet<>(); // 單向導航
    // getters/setters
}

public class Card {
    private String id;
    private float balance;
    // 無 user 反向引用
    // getters/setters
}

資料庫結構

CREATE TABLE myuser (
    id VARCHAR(50) NOT NULL PRIMARY KEY,
    name VARCHAR(100)
);

CREATE TABLE mycard (
    id VARCHAR(50) NOT NULL PRIMARY KEY,
    balance FLOAT(7,2),
    userId VARCHAR(50)  -- 外鍵,允許 NULL
);

關鍵:userId 不可設 NOT NULL,因 Hibernate 先 INSERT Card (userId 為 NULL),再 UPDATE 填入 userId。

映射檔 (User.hbm.xml)

<class name="User" table="myuser">
    <id name="id"><generator class="uuid.hex"/></id>
    <property name="name"/>
    <set name="cards" inverse="false" cascade="all">
        <key column="userId"/>
        <one-to-many class="Card"/>
    </set>
</class>

關鍵屬性解析

執行 SQL 追蹤

Hibernate: INSERT INTO myuser (name, id) VALUES (?, ?)
Hibernate: INSERT INTO mycard (balance, id) VALUES (?, ?)  -- userId 為 NULL
Hibernate: INSERT INTO mycard (balance, id) VALUES (?, ?)
Hibernate: INSERT INTO mycard (balance, id) VALUES (?, ?)
Hibernate: UPDATE mycard SET userId=? WHERE id=?  -- 回寫外鍵
Hibernate: UPDATE mycard SET userId=? WHERE id=?
Hibernate: UPDATE mycard SET userId=? WHERE id=?

經典陷阱:inverse="true" 導致外鍵不寫入

許多教材建議一對多設 inverse="true" 由多方維護以提升效能。但 單向關聯無反向導航,Card 物件不知道 User,Hibernate 無法在 INSERT 時填入 userId,且 inverse="true" 告訴 Hibernate「不由我維護」,結果 userId 永遠為 NULL。

鐵律:單向一對多 inverse 必須為 false (或省略)。雙向一對多才在集合端設 inverse="true"、多方 many-to-one 維護。

完整測試程式碼

專案結構圖

資料庫表結構圖

// DAO 測試
public void insert() {
    Set<Card> cards = new HashSet<>();
    cards.add(new Card(7641.96f));
    cards.add(new Card(654.8f));
    cards.add(new Card(3650f));

    User user = new User();
    user.setName("Tom");
    user.setCards(cards);

    Session s = HibernateUtil.getSession();
    Transaction tx = s.beginTransaction();
    s.save(user);  // cascade="all" 自動存 cards
    tx.commit();
}

運行結果驗證

id: 402881e421d4d0be0121d4d20e140005
name: Tom
cards:
  cardId: 402881e421d4d0be0121d4d20e230006 balance: 7641.96
  cardId: 402881e421d4d0be0121d4d20e230007 balance: 3650.0
  cardId: 402881e421d4d0be0121d4d20e230008 balance: 654.8
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved
屬性值說明
inversefalse (預設)單向關聯 User 為關聯維護方,必須為 false。設 true 會導致 userId 永遠不寫入 (見下文陷阱)
cascadeall儲存 User 時自動儲存/更新/刪除關聯 Cards
key columnuserIdCard 表的外鍵欄位