Spring 数据 JPA 保存 child object ID 为 parent object

Spring Data JPA save child object with the ID of parent object

我有两个 object,一个 parent 和一个 child 如下:

@Entity
@Table(name="category")
public class CategoryModel {
    private @Id @GeneratedValue Long id;

    private String name;

    @OneToMany(mappedBy="category", cascade=CascadeType.PERSIST)
    private List<AttributeModel> attributes;
}

@Entity
@Table(name="attribute")
public class AttributeModel {
    private @Id @GeneratedValue Long id;

    private String name;

    @ManyToOne(cascade = CascadeType.ALL)
    @JoinColumn(name="category_id")
    private CategoryModel category;
}

我也有映射到这些模型的 dtos objects 但我省略了它们。 当我尝试使用此有效负载保存类别 object 时,属性值也在属性 table 中创建,但具有 null 类别 ID。

{
    "name":"Chemicals",
    "attributes":[
        {"name": "volume"}, {"name":"humidity"}
    ]
}

我该怎么做才能将我的属性值与之前创建的类别 ID 一起保存到数据库中?

首先,这个问题不是“Spring Data JPA”的问题,而是JPA(可能是Hibernate)的问题。


分析

由于您遗漏了控制器和 JSON 映射的代码,我不得不猜测一下:

  • 事实1:类别属性之间的关系由属性AttributeModel.category控制,但是不是 CategoryModel.attributes。 (这就是 JPA 的工作原理)。
  • 观察 2: 您的 JSON 对象定义 CategoryModel.attributes(即与 JPA 的工作方式相反)。

在不知道您的 JSON 映射配置和控制器代码的情况下,我 猜测 问题是:您的 JSON 映射器反序列化 JSON 对象时不设置 AttributeModel.category 字段。


解决方案

所以你需要指示 JSON 映射器在反序列化期间设置 AttributeModel.category 字段。如果你使用 Jackson,你可以使用:

  • @JsonManagedReference
  • @JsonBackReference
@Entity
@Table(name="category")
public class CategoryModel {
    ...    

    @JsonManagedReference
    @OneToMany(mappedBy="category", cascade=CascadeType.PERSIST)
    private List<AttributeModel> attributes;
}
@Entity
@Table(name="attribute")
public class AttributeModel {
    ...

    @JsonBackReference
    @ManyToOne(cascade = CascadeType.ALL)
    @JoinColumn(name="category_id")
    private CategoryModel category;
}

我通过如下手动设置子对象对父对象的引用解决了这个问题:

public Long createCategory(CategoryDto categoryDto) {
    CategoryModel categoryModel = categoryDto.toModel(true,true);
    categoryModel.getAttributes().forEach(a -> a.setCategory(categoryModel));
    return categoryRepository.save(categoryModel).getId();
}