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

使用Hibernate/Spring Boot测试HSQL数据库时遇唯一约束违反问题

嘿,看起来你在Hibernate/Spring Boot和HSQL的实体关联上踩了个典型的坑,我来帮你拆解问题根源和解决办法:

问题核心原因

你遇到的integrity constraint violation: unique constraint or index violation; SYS_PK_10126 table: USER异常,主要是由两个关键配置错误引发的:

1. 级联操作触发User重复保存

你的Shoebox实体里,@ManyToOne注解设置了cascade = CascadeType.ALL:

@ManyToOne(cascade = CascadeType.ALL)
@JoinColumn(name="OwnerID")
@JsonBackReference(value="shoebox_owner")
User owner;

CascadeType.ALL包含了PERSIST(保存)操作,这意味着当你调用shoeboxes.save(sb)时,Hibernate会自动尝试级联保存关联的User对象。但你在测试代码里已经提前通过users.save(user)把ID=1的User存入数据库了,此时Hibernate再尝试保存同一个User,就会触发数据库的主键唯一约束冲突——毕竟数据库里已经有这条记录了。

2. 双向关联映射缺失导致多余中间表(附带脚本错误)

在User实体的@OneToMany注解中,你没有指定mappedBy属性:

@OneToMany(fetch = FetchType.LAZY)
@JsonManagedReference(value="shoebox_owner")
private List<Shoebox> shoeboxes;

mappedBy是双向关联的关键,用来指定关系的维护端(这里应该是Shoebox的owner字段)。缺失这个配置会让Hibernate误以为这是个单向关联,自动生成多余的USER_SHOEBOX中间表。更糟的是,你的数据库脚本里这个中间表的外键还写错了:

CONSTRAINT FKIR9SOKRCOQ33LCQTNR0LDXO93 FOREIGN KEY(USER_ID) REFERENCES PUBLIC.SHOEBOXUSER(ID)

这里应该指向PUBLIC.USER(ID),而不是不存在的SHOEBOXUSER表——虽然这个错误不是当前异常的直接原因,但后续肯定会引发关联操作失败。


解决方案

第一步:调整级联策略

把Shoebox的@ManyToOne级联类型改成更合理的选项,比如直接去掉CascadeType.ALL(因为你已经提前保存了User):

@ManyToOne
@JoinColumn(name="OwnerID")
@JsonBackReference(value="shoebox_owner")
User owner;

如果确实需要级联操作(比如更新User时同步更新关联的Shoebox),可以只保留必要的级联类型:

@ManyToOne(cascade = {CascadeType.MERGE, CascadeType.REFRESH})
@JoinColumn(name="OwnerID")
@JsonBackReference(value="shoebox_owner")
User owner;

第二步:修复双向关联映射

在User的@OneToMany注解中添加mappedBy="owner",明确关系由Shoebox的owner字段维护:

@OneToMany(fetch = FetchType.LAZY, mappedBy = "owner")
@JsonManagedReference(value="shoebox_owner")
private List<Shoebox> shoeboxes;

这样Hibernate就不会再生成多余的USER_SHOEBOX表,直接用Shoebox的OwnerID作为外键关联。

第三步:修正数据库脚本错误

如果你保留了中间表(其实修复关联后不需要了),把外键指向改成正确的User表:

CONSTRAINT FKIR9SOKRCOQ33LCQTNR0LDXO93 FOREIGN KEY(USER_ID) REFERENCES PUBLIC.USER(ID)

或者直接删除USER_SHOEBOX表的创建语句,因为双向关联修复后不需要这个中间表了。


额外小建议
  • 尽量避免手动设置主键值(比如你直接给User设ID=1),可以用@GeneratedValue让Hibernate自动生成主键,减少主键冲突风险:
@Id
@Column(name = "ID")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long ID;
  • 测试时开启Hibernate的SQL日志,能直观看到执行的SQL语句,更容易定位这类持久化问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 03:52:26