Spring Boot中如何同时保存父实体及子实体列表?
开发一个包含Exam与ExamInputs一对多关系的项目,保存Exam实体时,通过请求Payload传入ExamInputs列表,期望同时保存父、子实体,无需先存父再存子。但目前请求时ExamInputs的EXAM_ID字段为null,原因是父子实体同时创建时,ExamInput对象未关联父实体。当前通过手动调用examInput.setExam(exam)实现功能,想知道能否不手动设置父对象关联完成保存,同时确认哪种cascade类型最适合该场景。
ExamEntity 代码
@Entity public class ExamEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(columnDefinition = "Long") private long id; @Column(nullable = false) private String examType; @OneToMany(cascade = CascadeType.ALL, mappedBy = "exam") private List<ExamInputEntity> examInputs; }
ExamInputEntity 代码
@Entity public class ExamInputEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private long id; @Column(nullable = false) private String inputName; @Column(nullable = false) private String inputValue; @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "exam_id") private ExamEntity exam; }
请求Payload
{ "examType":"Test", "examInputs": [ { "inputName": "test", "inputValue": "test" } ] }
1. 能否不手动设置父对象关联?
不行。JPA中双向关联的维护端是@ManyToOne所在的子实体(ExamInputEntity),@OneToMany的mappedBy属性已经明确指定由子实体的exam字段维护关联关系——只有当子实体设置了父实体引用时,JPA才会自动填充exam_id外键字段。
如果不想在业务代码里手动循环设置examInput.setExam(exam),可以在ExamEntity中添加一个辅助方法,在添加子实体时自动维护关联:
public void addExamInput(ExamInputEntity examInput) { if (examInputs == null) { examInputs = new ArrayList<>(); } examInputs.add(examInput); examInput.setExam(this); // 自动绑定父实体 }
接收Payload转换为实体后,只需调用这个方法批量添加子实体即可,无需逐个手动关联。
2. 最适合的cascade类型
当前使用的CascadeType.ALL已经满足需求,若需更精准控制可按需选择:
CascadeType.PERSIST:仅在保存父实体时级联保存子实体,适合仅新增的场景CascadeType.MERGE:级联更新,适合修改场景CascadeType.REMOVE:级联删除,删除父实体时自动删除关联子实体
如果业务场景需要同时支持新增、修改、删除子实体,CascadeType.ALL是最优选择;若只是单纯新增场景,CascadeType.PERSIST足够。
另外可关注@OneToMany的orphanRemoval属性:设为true时,若子实体从父实体的集合中移除,JPA会自动删除该子实体,可根据业务需求决定是否开启。
内容的提问来源于stack exchange,提问作者Iago Duarte

