使用带有父 ID 的 @PathVariable 通过 REST 保存子项
Using @PathVariable with Parent ID to Save Child over REST
Spring 引导,我有一个 Rest 控制器,我试图在其中保存一个带有父对象的对象。
@Entity
public class Parent {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@OneToMany(mappedBy = "parent", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
@JsonManagedReference()
private List<Child> children;
// getters / setters
}
@Entity
public class Child {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@JsonView(Views.Summary.class)
private Long id;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "parent_id")
@NotNull
@JsonBackReference()
private Parent parent;
// getters / setters
}
由于 @JsonBackReference
,Jackson 不会序列化完全包含父对象的子对象。所以在我的测试中做类似下面的事情是行不通的...
Child child = new Child()
child.setParent(parent)
ObjectMapper mapper = new ObjectMapper()
String payload = mapper.writeValueAsString(child);
我最终得到以下 JSON:
{ "id": null }
我真正想要的是
{ "id": null, parent: { "id": 1 } }
所以我决定做的是将父 ID 添加为 @PathVariable
并将其传递到我的服务方法中,然后手动设置它。问题是,由于 Parent
在 Child 中不能为空,并且我正在使用 @Valid
验证 Child 我什至没有进入我的方法的关键,以便我可以处理 Parent ID .
@RequestMapping(value = "/{parentId}/children", method = RequestMethod.POST)
public void save(@PathVariable Long parentId, @Valid @RequestBody Child child, BindingResult bindingResult, HttpServletRequest request, HttpServletResponse response) {
if (bindingResult.hasErrors()) {
throw new InvalidRequestException("Invalid Child", bindingResult);
}
this.storeService.save(parentId, child);
response.setHeader("Location", request.getRequestURL().append("/").append(child.getId()).toString());
}
欢迎提出任何建议。
您的 JPA 模型(如您所写)表明 Parent
object 包含 Child
object 的列表。此外,使用 cascade = CascadeType.ALL
表示这是应该管理 Child
object 的 Parent
object,而不是相反。
换句话说,如果你想保存你的数据,你需要:
- 获取或创建
Parent
object(好的)
- 创建一个
Child
object(好的)
- 设置child的parent(好)
- 将 child 添加到 parent <-- 你错过了这一部分和下一部分
- 保存 parent(而不是直接保存 child)
每次需要更新children时,在parent中修改,然后保存parent.
这就是为什么在序列化 child 中具有 parent 的 ID 无关紧要。
此外,我建议您将 orphanRemoval=true
添加到 @OneToMany
注释中,这样 children 不再链接到他们的 parent 应该被删除嗯
更新(澄清):
如果您正在使用级联(如果是级联 ALL,则更多),您不会告诉您的应用程序您想要更新 Child
实例。您要做的是更新 parent 中的 child,然后保存 parent。级联将完成剩下的工作。
Spring 引导,我有一个 Rest 控制器,我试图在其中保存一个带有父对象的对象。
@Entity
public class Parent {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@OneToMany(mappedBy = "parent", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
@JsonManagedReference()
private List<Child> children;
// getters / setters
}
@Entity
public class Child {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@JsonView(Views.Summary.class)
private Long id;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "parent_id")
@NotNull
@JsonBackReference()
private Parent parent;
// getters / setters
}
由于 @JsonBackReference
,Jackson 不会序列化完全包含父对象的子对象。所以在我的测试中做类似下面的事情是行不通的...
Child child = new Child()
child.setParent(parent)
ObjectMapper mapper = new ObjectMapper()
String payload = mapper.writeValueAsString(child);
我最终得到以下 JSON:
{ "id": null }
我真正想要的是
{ "id": null, parent: { "id": 1 } }
所以我决定做的是将父 ID 添加为 @PathVariable
并将其传递到我的服务方法中,然后手动设置它。问题是,由于 Parent
在 Child 中不能为空,并且我正在使用 @Valid
验证 Child 我什至没有进入我的方法的关键,以便我可以处理 Parent ID .
@RequestMapping(value = "/{parentId}/children", method = RequestMethod.POST)
public void save(@PathVariable Long parentId, @Valid @RequestBody Child child, BindingResult bindingResult, HttpServletRequest request, HttpServletResponse response) {
if (bindingResult.hasErrors()) {
throw new InvalidRequestException("Invalid Child", bindingResult);
}
this.storeService.save(parentId, child);
response.setHeader("Location", request.getRequestURL().append("/").append(child.getId()).toString());
}
欢迎提出任何建议。
您的 JPA 模型(如您所写)表明 Parent
object 包含 Child
object 的列表。此外,使用 cascade = CascadeType.ALL
表示这是应该管理 Child
object 的 Parent
object,而不是相反。
换句话说,如果你想保存你的数据,你需要:
- 获取或创建
Parent
object(好的) - 创建一个
Child
object(好的) - 设置child的parent(好)
- 将 child 添加到 parent <-- 你错过了这一部分和下一部分
- 保存 parent(而不是直接保存 child)
每次需要更新children时,在parent中修改,然后保存parent.
这就是为什么在序列化 child 中具有 parent 的 ID 无关紧要。
此外,我建议您将 orphanRemoval=true
添加到 @OneToMany
注释中,这样 children 不再链接到他们的 parent 应该被删除嗯
更新(澄清):
如果您正在使用级联(如果是级联 ALL,则更多),您不会告诉您的应用程序您想要更新 Child
实例。您要做的是更新 parent 中的 child,然后保存 parent。级联将完成剩下的工作。