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

Hibernate 單向一對多:外鍵非空約束導致的插入異常解析

     編輯:Java 相關知識

在使用 Hibernate 實現單向一對多關聯時,常遇到 Field 'sid' doesn't have a default value 異常。本文透過具體案例剖析根因:inverse="false" (預設) 導致 Hibernate 分兩條 SQL 執行插入與更新,與資料庫外鍵 NOT NULL 約束衝突,並給出正確配置方案。

問題現場

異常堆疊:

java.sql.SQLException: Field 'sid' doesn't have a default value
Hibernate: insert into hbql.score (score, type) values (?, ?)
Hibernate: update hbql.score set sid=? where id=?

測試代碼:

Student student = new Student("huizhi", "2708", 200);
Score score1 = new Score(98, "1");
Score score2 = new Score(90, "2");
Set<Score> set = new HashSet<>();
set.add(score1); set.add(score2);
student.setScores(set);
saveObject(student);

映射配置分析

Student.hbm.xml (一端)

<set name="scores" table="score" cascade="save-update" inverse="false">
    <key>
        <column name="sid" not-null="true" />
    </key>
    <one-to-many class="score.Score" />
</set>

Score.hbm.xml (多端)

<class name="score.Score" table="score">
    <id name="id"><generator class="native"/></id>
    <property name="score"/>
    <property name="type"/>
    <!-- 無對 Student 的反向引用 -->
</class>

資料庫結構

CREATE TABLE `score` (
  `id` int(10) unsigned NOT NULL auto_increment,
  `score` int(10) unsigned NOT NULL,
  `type` varchar(20) NOT NULL,
  `sid` int(10) unsigned NOT NULL,  -- 外鍵非空
  PRIMARY KEY (`id`),
  CONSTRAINT `FK_score_1` FOREIGN KEY (`sid`) REFERENCES `student` (`id`)
) ENGINE=InnoDB;

根因深度解析

<set inverse="false"> (預設) 表示**關聯關係由一端 (Student) 維護**。Hibernate 執行流程:

  1. 先插入 Score 記錄,**外鍵 sid 為 NULL** (因多端無反向引用,不知學生 ID)。
  2. 插入 Student 並取得生成 ID。
  3. 再執行 UPDATE score SET sid = ? WHERE id = ? 更新外鍵。

但資料庫定義 sid NOT NULL,步驟 1 直接違反約束,拋出異常。

解決方案

方案一:修改資料庫約束 (不推薦)

將 sid 改為 NULL DEFAULT NULL。但違反關聯完整性設計,不建議。

方案二:設定 inverse="true" (推薦)

將關聯維護權移交給多端 (Score),但單向一對多**無法**在多端維護關聯 (因無反向引用屬性)。故單向一對多**必須**保持 inverse="false"。

方案三:使用雙向一對多 (最佳實踐)

在 Score 加入 Student student 屬性與對應映射:

<!-- Score.hbm.xml -->
<many-to-one name="student" class="student.Student" column="sid" not-null="true"/>

<!-- Student.hbm.xml -->
<set name="scores" inverse="true" cascade="save-update">
    <key column="sid" not-null="true"/>
    <one-to-many class="score.Score"/>
</set>

此時 inverse="true",由多端控制外鍵插入,Hibernate 會在插入 Score 時直接帶上 sid,單條 SQL 完成,完美解決問題。

方案四:若堅持單向,調整映射 (可行但不優雅)

在 <key> 移除 not-null="true",資料庫同步修改為允許 NULL。但語義上削弱了強制關聯約束。

總結

場景inverse 設定外鍵策略建議
雙向一對多inverse="true" (多端維護)NOT NULL 可行✅ 最佳
單向一對多inverse="false" (一端維護)需允許 NULL 或雙向改造⚠️ 建議改雙向

單向一對多雖簡單,但受限於 Hibernate 持久化順序,易與資料庫約束衝突。實務上**強烈建議採用雙向關聯**,將關聯維護權交給多端,兼顧效能與數據完整性。

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