使用带有父ID的@PathVariable将孩子保存在REST上

时间:2016-05-06 14:08:08

标签: json rest spring-boot http-post

Spring Boot,我有一个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对象,包括Parent。所以在我的测试中做类似以下的事情是行不通的......

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中不能为null,并且我使用@Valid验证了Child,所以我甚至没有进入我的方法的关键所以我可以处理父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());
    }

任何建议都非常受欢迎。

1 个答案:

答案 0 :(得分:2)

您的JPA模型(正如您所写)表示Parent对象包含Child个对象的列表。此外,使用cascade = CascadeType.ALL表示这是应该管理Parent对象的Child对象,而不是相反。

换句话说,如果您想保存数据,则需要:

  • 获取或创建Parent对象(确定)
  • 创建一个Child对象(确定)
  • 设置孩子的父母(确定)
  • 将孩子添加到父级&lt; - 您缺少此部分和下一部分
  • 保存父母(而不是孩子直接)

每次需要更新子项时,请在父项中进行修改,然后保存父项。

这就是为什么这与序列化孩子中父母的id无关。

此外,我建议您将orphanRemoval=true添加到@OneToMany注释中,以便不再删除与其父级不相关的子项

更新(澄清): 如果您正在使用级联(如果它是级联ALL,则更多),您不会告诉应用程序您要更新Child实例。您所做的是更新其父级中的子级,然后保存父级。级联将完成其余的工作。