我正在尝试使用JPA(与Hibernate一起)来保存2个实体。 Spring数据提供了接口,但我认为这并不重要。
我有一个名为“模型”的主要实体。该模型具有许多链接的“参数”实体。我正在写一种保存模型及其参数的方法。
这是方法:
private void cascadeSave(Model model) {
modelRepository.save(model);
for (ParameterValue value : model.getParameterValues()) {
parameterValueRepository.save(value);
}
}
这是问题所在
当我加载一个已经存在的模型时,请向其中添加一些新参数,然后调用此方法来保存它们,这会发生一些奇怪的事情:
在第一次保存(modelRepository.save)之前,这是调试时模型的数据:
模型有2个参数,并带有填充值(名称和模型已填充)。
现在,在将模型保存到我的方法中的第一次保存之后,就会发生这种情况。请注意,对象引用是不同的,因此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;
}
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。实际上,它们只是被全新的参数所取代。
答案 0 :(得分:5)
我想您正在使用Spring Data Jpa作为您的modelRepository。这表明有以下后果。
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:)