Hibernate单表继承+OneToMany映射保存SCQ触发mcq_qid非空约束报错
问题核心原因
- 重复的一对多关联导致多余外键生成
你在Scq、Mcq两个子类中分别定义了@OneToMany关联的choices字段,Hibernate处理单向一对多关联时,会自动在Choice对应的数据表中生成两个外键列:scq_qid(关联Scq主键)、mcq_qid(关联Mcq主键)。当你保存Scq实体时,Hibernate只会填充scq_qid的值,mcq_qid默认留空,触发数据库非空约束报错。 - 注解逻辑冲突
choices字段同时标注了@OneToMany和@Type(type = "jsonb"),两个注解逻辑完全矛盾:@OneToMany是要关联独立的Choice实体、存储关联关系,jsonb类型是要将整个列表序列化为JSON字符串存在当前表的字段中,二者不能同时生效。
解决方案
根据你的业务需求二选一即可:
方案1:将choices作为JSON字段存储(不需要Choice独立成实体)
如果不需要对Choice做单独的查询、关联操作,直接将选项以JSON格式存在Question表中即可:
- 去掉
Scq、Mcq类中choices字段上的@OneToMany注解 - 去掉
Choice类上的@Entity注解,改为@Embeddable注解 - 可选择将
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做单独的管理、查询,就用标准的一对多关联:
- 去掉
Scq、Mcq类中choices字段上的@Type(jsonb)、@Column(columnDefinition = "jsonb")注解 - 将
choices字段抽到父类Question中统一管理,Hibernate只会在Choice表生成一个question_qid外键,不会出现多个外键空值问题 - 可选优化:将单向一对多改为双向关联,避免Hibernate生成中间关联表,提升性能
// 父类Question新增字段 @OneToMany(cascade = CascadeType.PERSIST, fetch = FetchType.EAGER, mappedBy = "question") private List<Choice> choices; // Choice类新增关联字段 @ManyToOne private Question question;
内容的提问来源于stack exchange,提问作者Nimisha Shukla
相关产品推荐
相关产品推荐

