Spring Data JPA(H2) OneToMany保存时触发唯一约束异常
问题原因
这个报错是JPA实体关联配置和业务逻辑不匹配导致的,具体逻辑如下:
- 先看Hibernate自动生成的DDL,中间表
complex_entity_entities上存在一个唯一约束UK_nxr507qlfqwm2vk6gqes2b3lc,限制了entities_id(关联DictEntity主键的字段)的值不能重复。这个约束的实际含义是:同一个DictEntity最多只能关联一个ComplexEntity。 - 这个约束来自你当前的
@OneToMany配置:你在ComplexEntity类的properties字段上标记@OneToMany时,既没有配置mappedBy声明双向关联的从属端,也没有手动定义@JoinTable调整中间表约束。对于无额外配置的单向@OneToMany,Hibernate默认会通过中间表实现关联,并且默认给多端实体的关联外键加唯一约束,按「每个多端实体仅能从属一个一端实体」的普通一对多场景处理。 - 你的业务代码逻辑是把id=1的
DictEntity(foo)同时关联给m1、m2两个ComplexEntity:保存m1时已经向中间表插入了(complex_entity_id=1, entities_id=1)的记录,保存m2时需要再插入(complex_entity_id=2, entities_id=1)的记录,直接触发了entities_id的唯一约束冲突,抛出异常。
修复方案
根据你实际的业务关系选择对应配置即可:
- 如果业务要求一个DictEntity可以被多个ComplexEntity复用,一个ComplexEntity可以持有多个DictEntity,这本质是多对多关系,直接将
properties字段上的@OneToMany改为@ManyToMany即可。修改后Hibernate生成的中间表不会再给entities_id加唯一约束,同一个DictEntity可以关联多个ComplexEntity,不会触发冲突。 - 如果业务确实是一对多关系:一个DictEntity只能归属于一个ComplexEntity,一个ComplexEntity可以持有多个DictEntity,那你的业务代码逻辑本身有问题,不能把已经关联到m1的foo再重复关联给m2。这种场景更推荐做双向一对多配置:在
DictEntity类中添加@ManyToOne修饰的ComplexEntity类型字段,然后在ComplexEntity的@OneToMany注解上添加mappedBy = "你在DictEntity中定义的ComplexEntity字段名",这种配置不会生成中间表,会直接在dict_entity表上加外键关联ComplexEntity,执行效率更高。
内容的提问来源于stack exchange,提问作者Ivan Zelenskyy
相关产品推荐
相关产品推荐

