Spring-Data-Jpa在保持后清除链接实体的所有参数

geo*_*ydv 3 java hibernate jpa spring-data-jpa

我正在尝试使用JPA(使用Hibernate)来保存2个实体.Spring数据提供了界面,但我认为这并不重要.

我有一个名为'Model'的主要实体.此模型链接了许多"参数"实体.我正在编写一种方法来保存模型及其参数.

这是方法:

  private void cascadeSave(Model model) {

    modelRepository.save(model);

    for (ParameterValue value : model.getParameterValues()) {
        parameterValueRepository.save(value);
    }
}
Run Code Online (Sandbox Code Playgroud)

这就是问题:

当我加载之前已经存在的模型时,向它添加一些新参数然后调用此方法来保存它们发生的奇怪事情:

在第一次保存(modelRepository.save)之前,这是调试时模型的数据:

在此输入图像描述

该模型有2个参数,填充值(名称和模型已填充).

现在,在保存模型后,我的方法中的第一个保存就会发生这种情况.请注意,对象引用是一个不同的对象引用,所以Hibernate必须做一些神奇的事情并重新创建值而不是让它们孤立:

在此输入图像描述

出于某种原因,hibernate清除了集合中参数的所有属性.

现在,当在以下代码中保存新参数时,由于非空约束等原因而失败.

我的问题:为什么hibernate会清除所有字段?

以下是相关映射:

的parameterValue

@Entity
@Table(name = "tbl_parameter_value")
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "PARAMETER_TYPE")
public abstract class ParameterValue extends AbstractBaseObject {

    @Column(nullable = false)
    @NotBlank
    private String name;
    private String stringValue;
    private Double doubleValue;
    private Integer intValue;
    private Boolean booleanValue;
    @Enumerated(EnumType.STRING)
    private ModelType modelParameterType;
    @Column(precision = 7, scale = 6)
    private BigDecimal bigDecimalValue;
    @Lob
    private byte[] blobValue;

    ParameterValue() {
    }

    ParameterValue(String name) {
        this.name = name;
    }
Run Code Online (Sandbox Code Playgroud)

ModelParameterValue

@Entity
@DiscriminatorValue(value = "MODEL")
public class ModelParameterValue extends ParameterValue {

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "model_id", foreignKey = @ForeignKey(name = "FK_VALUE_MODEL"))
    private Model model;

    ModelParameterValue() {
        super();
    }

    ModelParameterValue(String name) {
        super(name);
    }
Run Code Online (Sandbox Code Playgroud)

模型

@Entity
@Table(name = "tbl_model")
public class Model extends AbstractBaseObject implements Auditable {

    @OneToMany(fetch = FetchType.LAZY, mappedBy = "model")
    private Set<ModelParameterValue> parameterValues = new HashSet<>();
Run Code Online (Sandbox Code Playgroud)

编辑

我能用最小的例子重现这一点.如果你更换所有弹簧数据,这就是幕后发生的事情(em是JPA EntityManager):

public Model simpleTest() {

    Model model = new Model("My Test Model");
    em.persist(model);

    model.addParameter(new Parameter("Param 1"));
    em.merge(model);

    for (Parameter child : model.getParameters()) {
        em.persist(child);
    }

    return model;
}
Run Code Online (Sandbox Code Playgroud)

执行合并时,参数的所有属性都设置为null.它们实际上只是被全新的参数所取代.

Emi*_*ski 5

我猜你使用的是Spring Data Jpa作为你的modelRepository.这表明了以下后果.

Spring Repository Save

S保存(S实体)

保存给定的实体.使用返回的实例进行进一步操作,因为save操作可能已完全更改了实体实例.

所以这是你遇到的正常行为.

代码应更改为:

model = modelRepository.save(model);

for (ParameterValue value : model.getParameterValues()) {
    parameterValueRepository.save(value);
}
Run Code Online (Sandbox Code Playgroud)

编辑:

我认为你的保存功能在意义上是破坏的,你是向后做的.你可以在你的关系中使用CascadeType,或者你必须先保存孩子.

级联

Cascade的工作方式类似于"如果您保存父级,保存子级,如果您更新父级,则更新子级......"

所以我们可以像你那样对你的关系进行级联:

@Entity
@Table(name = "tbl_model")
public class Model extends AbstractBaseObject implements Auditable {

    @OneToMany(fetch = FetchType.LAZY, mappedBy = "model", cascade = CascadeType.ALL)
    private Set<ModelParameterValue> parameterValues = new HashSet<>();
Run Code Online (Sandbox Code Playgroud)

然后才这样保存

  private void cascadeSave(Model model) {

    modelRepository.save(model);
    //ParamValues will be saved/updated automaticlly if your model has changed

}
Run Code Online (Sandbox Code Playgroud)

自下而上保存

第二种选择只是首先保存params然后用它们建模.

  private void cascadeSave(Model model) {

    model.setParameterValues(
      model.getParameterValues().stream()
       .map(param ->  parameterValueRepository.save(param))
       .collect(Collectors.toSet())
   );
    modelRepository.save(model);
}
Run Code Online (Sandbox Code Playgroud)

我没有检查我的编译器中的第二个代码,但想法是首先保存子项(ParamValues),将其放入模型然后保存模型:)