JPA多对多带额外字段关联表保存报foo_id空约束错误怎么解决?
问题解决方案
错误根因
FooBar实体中两个关联字段的@JoinColumn都加了insertable = false, updatable = false配置,该配置会让JPA在插入/更新数据时,忽略这两个关联字段对应的数据库列映射,不会自动把关联的Foo的fooId、Bar的id写入FooBar表的对应列。- 业务代码中传入的
Foo是自行构建的临时实例,并非从数据库查询得到的持久态实例,就算移除insertable限制,JPA也无法正确识别关联关系,甚至会尝试新增Foo记录引发其他异常。
解决方案
可根据业务场景二选一:
方案1:保留只读关联配置(推荐不需要通过FooBar修改关联关系的场景)
- 先给
FooBar实体补充对应物理列的映射字段:
@Entity public class FooBar { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long fooBarId; // 新增物理列映射字段 @Column(name = "fooId") private UUID fooId; @Column(name = "barId") private Long barId; @JoinColumn(name = "fooId", referencedColumnName = "fooId", insertable = false, updatable = false) @ManyToOne(fetch = FetchType.LAZY) private Foo foo; @Column private String x; @Column private String y; @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "barId", referencedColumnName = "id" ,insertable = false, updatable = false) private Bar bar; // 省略getter、setter、builder方法补充 }
- 调整业务代码,先查询已存在的
Foo实例,显式给FooBar的物理列赋值:
void doStuff(UUID theFooUuid) { // 查询已有持久态Foo实例 Foo existingFoo = fooRepository.findByFooId(theFooUuid).orElseThrow(); List<FooBar> refs = new ArrayList<>(); FooBar fooBar = FooBar.builder() .x("x") .y("y") .foo(existingFoo) .fooId(existingFoo.getFooId()) // 显式赋值fooId .build(); refs.add(fooBar); Bar bar = new Bar(); bar.setFoobars(refs); // 先保存Bar拿到生成的主键 Bar savedBar = barRepository.saveAndFlush(bar); // 给所有FooBar赋值barId refs.forEach(item -> item.setBarId(savedBar.getId())); // 保存FooBar fooBarRepository.saveAllAndFlush(refs); }
方案2:移除只读限制(推荐需要通过FooBar调整关联关系的场景)
- 去掉
FooBar实体两个@JoinColumn的insertable = false, updatable = false配置:
@Entity public class FooBar { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long fooBarId; @JoinColumn(name = "fooId", referencedColumnName = "fooId") @ManyToOne(fetch = FetchType.LAZY) private Foo foo; @Column private String x; @Column private String y; @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "barId", referencedColumnName = "id") private Bar bar; }
- 业务代码中传入持久态的
Foo实例即可,JPA会自动处理关联列赋值:
void doStuff(UUID theFooUuid) { Foo existingFoo = fooRepository.findByFooId(theFooUuid).orElseThrow(); List<FooBar> refs = new ArrayList<>(); refs.add(FooBar.builder() .x("x") .y("y") .foo(existingFoo) .build()); Bar bar = new Bar(); bar.setFoobars(refs); barRepository.saveAndFlush(bar); }
内容的提问来源于stack exchange,提问作者Aakanksha Sharma
相关产品推荐
相关产品推荐

