單向一對多:只在「一方」 (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>
關鍵屬性解析
| 屬性 | 值 | 說明 |
inverse | false (預設) | 單向關聯 User 為關聯維護方,必須為 false。設 true 會導致 userId 永遠不寫入 (見下文陷阱) |
cascade | all | 儲存 User 時自動儲存/更新/刪除關聯 Cards |
key column | userId | Card 表的外鍵欄位 |
執行 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
- 上一篇文章: Hibernate 效能調優五大核心策略:從批次操作到抓取策略全解析
- 下一篇文章: Hibernate + MySQL 快速入門:純 Java SE 環境零配置實戰教學
Copyright ©
程式碼之家 All Rights Reserved