You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Hibernate单表继承+OneToMany映射保存SCQ触发mcq_qid非空约束报错

问题核心原因

  1. 重复的一对多关联导致多余外键生成
    你在Scq、Mcq两个子类中分别定义了@OneToMany关联的choices字段,Hibernate处理单向一对多关联时,会自动在Choice对应的数据表中生成两个外键列:scq_qid(关联Scq主键)、mcq_qid(关联Mcq主键)。当你保存Scq实体时,Hibernate只会填充scq_qid的值,mcq_qid默认留空,触发数据库非空约束报错。
  2. 注解逻辑冲突
    choices字段同时标注了@OneToMany和@Type(type = "jsonb"),两个注解逻辑完全矛盾:@OneToMany是要关联独立的Choice实体、存储关联关系,jsonb类型是要将整个列表序列化为JSON字符串存在当前表的字段中,二者不能同时生效。

解决方案

根据你的业务需求二选一即可:

方案1:将choices作为JSON字段存储(不需要Choice独立成实体)

如果不需要对Choice做单独的查询、关联操作,直接将选项以JSON格式存在Question表中即可:

  1. 去掉Scq、Mcq类中choices字段上的@OneToMany注解
  2. 去掉Choice类上的@Entity注解,改为@Embeddable注解
  3. 可选择将choices字段抽到父类Question中,避免子类重复定义

修改后示例代码:

// Question父类新增字段
@Type(type = "jsonb")
@Column(columnDefinition = "jsonb")
private List<Choice> choices;

// Choice类修改
@Embeddable
@TypeDefs({ @TypeDef(name = "jsonb", typeClass = JsonBinaryType.class),
        @TypeDef(name = "list-array", typeClass = ListArrayType.class) })
public class Choice implements Serializable {
    // 原有字段保留,去掉@Id相关注解即可
}

方案2:保留Choice为独立实体,走关联关系

如果需要对Choice做单独的管理、查询,就用标准的一对多关联:

  1. 去掉Scq、Mcq类中choices字段上的@Type(jsonb)、@Column(columnDefinition = "jsonb")注解
  2. 将choices字段抽到父类Question中统一管理,Hibernate只会在Choice表生成一个question_qid外键,不会出现多个外键空值问题
  3. 可选优化:将单向一对多改为双向关联,避免Hibernate生成中间关联表,提升性能
// 父类Question新增字段
@OneToMany(cascade = CascadeType.PERSIST, fetch = FetchType.EAGER, mappedBy = "question")
private List<Choice> choices;

// Choice类新增关联字段
@ManyToOne
private Question question;

内容的提问来源于stack exchange,提问作者Nimisha Shukla

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.30 09:18:03