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

时间:2018-10-16 09:10:15

标签: java hibernate jpa spring-data-jpa

我正在尝试使用JPA(与Hibernate一起)来保存2个实体。 Spring数据提供了接口,但我认为这并不重要。

我有一个名为“模型”的主要实体。该模型具有许多链接的“参数”实体。我正在写一种保存模型及其参数的方法。

这是方法:

  private void cascadeSave(Model model) {

    modelRepository.save(model);

    for (ParameterValue value : model.getParameterValues()) {
        parameterValueRepository.save(value);
    }
}

这是问题所在

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

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

enter image description here

模型有2个参数,并带有填充值(名称和模型已填充)。

现在,在将模型保存到我的方法中的第一次保存之后,就会发生这种情况。请注意,对象引用是不同的,因此Hibernate必须做了一些神奇的事情,并重新创建了值,而不是将它们放在一边:

enter image description here

出于某种原因,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;
    }

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);
    }

模型

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

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

编辑

我能够用一个最小的例子来重现这一点。 如果您替换所有的spring数据,这就是幕后发生的事情(例如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;
}

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

1 个答案:

答案 0 :(得分:5)

我想您正在使用Spring Data Jpa作为您的modelRepository。这表明有以下后果。

Spring Repository Save

  

S保存(S实体)

     

保存给定的实体。使用返回的   保存操作可能具有进一步操作的实例   完全更改了实体实例。

所以这是您遇到的正常行为。

代码应更改为:

model = modelRepository.save(model);

for (ParameterValue value : model.getParameterValues()) {
    parameterValueRepository.save(value);
}

编辑:

我认为您的保存功能在某种意义上是被破坏的,您可以反向执行。您可以在关系中使用CascadeType,或者必须先保存子级。

级联

级联的工作方式是“如果您保存父级,保存子级,如果更新父级,则更新子级...”

所以我们可以像这样把级联放在您的关系上:

@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<>();

然后仅保存

  private void cascadeSave(Model model) {

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

}

自下而上保存

第二个选项是先保存参数,然后用它们建模。

  private void cascadeSave(Model model) {

    model.setParameterValues(
      model.getParameterValues().stream()
       .map(param ->  parameterValueRepository.save(param))
       .collect(Collectors.toSet())
   );
    modelRepository.save(model);
}

我尚未在编译器中检查第二个代码,但其想法是先保存子代(ParamValues),将其放入Model,然后保存Model:)