在雙向一對多關聯映射中,Repeated column in mapping for entity 是高頻報錯。本文通過實例深度剖析異常產生機制,並給出四種修復方案與最佳實踐建議。
// Item.java
public class Item {
private int id;
private String name;
private Set<Bid> bids = new HashSet<>();
}
// Bid.java
public class Bid {
private int id;
private double amount;
private Item item;
}
<set name="bids" table="t_bid" cascade="save-update">
<key column="item_id" not-null="true"/> <!-- 關鍵點 -->
<one-to-many class="value.Bid"/>
</set>
<many-to-one name="item" class="value.Item" column="item_id" not-null="true" />
Item item = new Item(); item.setName("item");
Bid b1 = new Bid(); b1.setAmount(12.09); b1.setItem(item);
Bid b2 = new Bid(); b2.setAmount(11.98); b2.setItem(item);
Set<Bid> bids = new HashSet<>(); bids.add(b1); bids.add(b2);
item.setBids(bids);
session.save(item); // 拋出異常
org.hibernate.MappingException:
Repeated column in mapping for entity: value.Bid column: item_id
(should be mapped with insert="false" update="false")
<key not-null="true"> 的副作用在 <key> 元素上指定 not-null="true",告訴 Hibernate:「外鍵列 item_id 在資料庫層面不可為 NULL」。為滿足此約束,**Item 端(一端)會在 INSERT 語句中顯式包含 item_id 列**並賦值。
對比:
not-null:insert into t_bid (amount) values (?)not-null="true":insert into t_bid (amount, item_id) values (?, ?)<many-to-one> 默認行為Bid 端的 <many-to-one column="item_id"> 同樣會在 INSERT 中包含 item_id(即使值為 NULL)。
Hibernate 生成 SQL 變為:
insert into t_bid (item_id, item_id, amount) values (?, ?, ?)
列名重複 → 拋出 Repeated column 異常。
| 方案 | 修改位置 | 原理 | 優點 | 缺點/風險 |
|---|---|---|---|---|
1. 移除 not-null="true" |
Item.hbm.xml <key> |
一端不再產生 item_id 列,完全交給多端維護 |
最簡潔,符合「雙向關聯由多端維護」原則 | 資料庫層面失去 NOT NULL 約束(可用 DDL 彌補) |
2. 多端加 insert="false" update="false" |
Bid.hbm.xml <many-to-one> |
多端放棄寫入權,改由一端控制 | 保留資料庫 NOT NULL 約束 | 違背「多端負責關聯維護」慣例,易誤解 |
3. 一端加 inverse="true" |
Item.hbm.xml <set> |
聲明關係由 Bid 端維護,Item 端只讀 | 標準雙向關聯最佳實踐,語義清晰 | 需確保 Bid 端正確設置 item 引用 |
| 4. 重命名外鍵列 | 任一映射文件 | 物理上避開列名衝突 | 快速止血 | 產生冗餘列,破壞資料模型完整性,**強烈不推薦** |
<set inverse="true">,語義明確「由多端控制關聯」,配合資料庫 DDL 維護 NOT NULL 約束。<key> 設置 not-null="true",除非極其確定單向關聯且無級聯保存。inverse="true" 的副作用與應對開啟 inverse="true" 後,若僅設置 item.setBids(bids) 而未設置 bid.setItem(item),外鍵將不會持久化。解決方式:
Item.addBid(Bid bid) 方法中雙向綁定:bids.add(bid); bid.setItem(this);@OneToMany(mappedBy="item") 註解,由提供者自動管理。