Spring Data JPA三张表关联插入异常:关联表外键为null
问题原因及解决方法
问题原因
- 双向关联
mappedBy属性不匹配:PEFT_LINE的@OneToMany中mappedBy = "PEFT_LINE",但PEFT_LINE_RELTN中对应的关联字段是peftItem,名称完全不符;同理PEFT_CHARGE的@OneToMany中mappedBy = "PEFT_CHARGE",对应PEFT_LINE_RELTN的字段是peftCharge,也存在名称不匹配问题。这会导致JPA无法正确识别关联关系的维护端。 - Repository泛型错误:PEFT_LINERepo继承
JpaRepository时,第一个泛型参数写为PeftId(主键字段),而非实体类PEFT_LINE,导致Repository无法正确操作实体数据。 - 级联更新导致字段覆盖:插入PEFT_CHARGE时,因为设置了
cascade=CascadeType.ALL,JPA会级联更新传入的PEFT_LINE_RELTN对象。但传入的PEFT_LINE_RELTN未携带关联的PEFT_LINE信息(peftItem为null),JPA会默认将该字段置空,从而覆盖之前已有的fk_peftId值。 - 字段命名不规范:实体字段使用
PeftId这类大驼峰命名,既不符合Java代码规范,也容易与数据库映射字段混淆,增加排查难度。
解决方法
1. 修正双向关联的mappedBy属性
调整实体类的关联映射,让mappedBy指向关联实体中实际的字段名:
PEFT_LINE实体:
@Entity public class PEFT_LINE { @Id private int peftId; // 改为小驼峰规范 private int procCode; @OneToMany(mappedBy = "peftItem", fetch = FetchType.LAZY, cascade = CascadeType.ALL) @JsonManagedReference("PEFT_LINE") private List<PEFT_LINE_RELTN> peftLineRelations; // 更清晰的命名 // getters and setters }
PEFT_CHARGE实体:
@Entity public class PEFT_CHARGE { @Id private int peftChrgId; // 小驼峰规范 private int activeCode; @OneToMany(mappedBy = "peftCharge", fetch = FetchType.LAZY, cascade = CascadeType.ALL) @JsonManagedReference("PEFT_CHARGE") private List<PEFT_LINE_RELTN> peftLineRelations; // getters and setters }
PEFT_LINE_RELTN实体:
@Entity public class PEFT_LINE_RELTN { @Id private int peftLineId; private int activeCd; @ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL) @JoinColumn(name = "fk_peftId") @JsonBackReference("PEFT_LINE") private PEFT_LINE peftItem; @ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL) @JoinColumn(name = "fk_peftchrgId") @JsonBackReference("PEFT_CHARGE") private PEFT_CHARGE peftCharge; // getters and setters }
2. 修正Repository泛型
将Repository的泛型参数改为对应实体类:
public interface PEFT_LINERepo extends JpaRepository<PEFT_LINE, Integer> { }
同时补充其他实体的Repository:
public interface PEFT_CHARGERepo extends JpaRepository<PEFT_CHARGE, Integer> { } public interface PEFT_LINE_RELTNRepo extends JpaRepository<PEFT_LINE_RELTN, Integer> { }
3. 正确维护关联关系(解决字段置空问题)
插入PEFT_CHARGE时,需要手动保留中间表原有的PEFT_LINE关联,避免被置空:
服务层处理逻辑:
@Service public class PeftChargeService { @Autowired private PEFT_CHARGERepo peftChargeRepo; @Autowired private PEFT_LINE_RELTNRepo peftLineReltnRepo; public void savePeftCharge(PEFT_CHARGE peftCharge) { for (PEFT_LINE_RELTN reltn : peftCharge.getPeftLineRelations()) { // 查询已存在的中间表记录,保留原PEFT_LINE关联 PEFT_LINE_RELTN existingReltn = peftLineReltnRepo.findById(reltn.getPeftLineId()).orElse(null); if (existingReltn != null) { reltn.setPeftItem(existingReltn.getPeftItem()); } // 设置当前PEFT_CHARGE关联 reltn.setPeftCharge(peftCharge); } peftChargeRepo.save(peftCharge); } }
或者修改请求结构,携带PEFT_LINE的关联信息:
[{ "peftChrgId" : 57, "activeCode" : 79, "peftLineRelations":[ { "peftLineId" : 652, "activeCd" : 1, "peftItem": { "peftId": 44 } } ] }]
4. 调整级联类型(可选)
如果不需要级联更新已有中间表数据,可以将cascade=CascadeType.ALL改为cascade=CascadeType.PERSIST,避免意外覆盖原有关联:
@OneToMany(mappedBy = "peftItem", fetch = FetchType.LAZY, cascade = CascadeType.PERSIST)
内容的提问来源于stack exchange,提问作者Ramya
相关产品推荐
相关产品推荐

