【问题标题】:Hibernate tries to persist child twice after setting parentHibernate 在设置父级后尝试两次持久化子级
【发布时间】:2017-05-11 18:54:35
【问题描述】:

我有这样的 Hibernate 映射:

家长:

@OneToMany(mappedBy = "parent")
@Valid
private Set<Assignment> assignments;

孩子:

@ManyToOne(cascade = {CascadeType.PERSIST})
@JoinColumn(name = "parent_id")
@Valid
private Parent parent;

我遇到了这样的情况:父级已经保存在数据库中,我得到了新的分配列表。我做了这样的事情(使用 Spring Data JPA):

parent.getAssignments().addAll(newAssignments);
parent.getAssignments().forEach(assignment -> assignment.setParent(parent));
parentRepository.save(parent);

我在基本实体中有@PrePersist 注释方法(由两者扩展),并通过验证检查createdDate,它在@PrePersist 中初始化是否不为空。基础实体包含:

@NotNull
@Column(name = "created_date")
protected LocalDateTime createdDate;

@PrePersist
public void prePersist() {
    setCreatedDate(LocalDateTime.now());
    setModifiedDate(LocalDateTime.now());
    validateEntity();
}

private void validateEntity() {
    Set<ConstraintViolation<BaseEntity>> violations = Validation.buildDefaultValidatorFactory().getValidator().validate(this);
    if(!violations.isEmpty()) {
        throw new RuntimeException(new ValidationMessageBuilder().build(violations));
    }
}

我检查了调试器下的对象,例如Assignment是对象@17609,Parent是@17608。

调用 save() 方法后引发异常。

java.lang.RuntimeException: may not be null => com.foo.bar.model.domain.Assignment.parent.assignments.createdDate.
at com.foo.bar.model.BaseEntity.validateEntity(BaseEntity.java:70)
at com.foo.bar.model.BaseEntity.prePersist(BaseEntity.java:58)

我已经调试并在Assignment实体的@PrePersist方法中为另一个对象调用它,即Assignment@17646,它包含Parent@17608,而parent包含具有一个元素的子元素集合,即@17609。

为什么hibernate会尝试持久化另一个对象?

【问题讨论】:

  • 我认为你应该只将assignements添加到parent并坚持parent,不需要将parent设置为assignement
  • 您遇到了什么样的异常...您可以发布吗?
  • @Antoniossss,如果我不设置 parent,hibernate 不会自动执行此操作,并尝试使用 parentId = null 保持分配,这是不正确的。如果我更改实体关系并在分配的父映射中添加 nullable="false" 则休眠尝试执行相同操作(将父设置为分配)并发生相同的异常。
  • @CarlitosWay 编辑并添加了异常。

标签: java hibernate jpa spring-data-rest


【解决方案1】:

这些是我对您的问题的观察:

  1. 抛出的异常不是来自 Hibernate... 由您的验证方法抛出:validateEntity(),因为您的 子作业有一个null createDate

  2. 您正在使用 EntityListeners 设置您的 createdDatemodifiedDate 并验证您的 bean 值...但是,似乎 (这是我的猜测)您的验证方法正在迭代 (并验证)在休眠之前很久的孩子分配 对它们执行@PrePersist

我的解决问题的建议:

  1. 使用此配置:

家长:

@OneToMany(mappedBy = "parent", CascadeType = {CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REMOVE})
private Set<Assignment> assignments;

孩子:

@ManyToOne
@JoinColumn(name = "parent_id")
private Parent parent;

BaseClass:将您的 prePersist 方法重命名为:

@PrePersist
@PreUpdate
public void prePersistOrUpdate() {
    setCreatedDate(LocalDateTime.now());
    setModifiedDate(LocalDateTime.now());
    validateEntity();
}
  1. 检查(并根据需要修改)您的基本验证方法。这个 方法应该只验证实例变量(如 createdDate) 并避免横向子对象 后来修改为EntityListener方法: prePersistOrUpdate() (注意,我删除了 @Valid 注释 因为我不知道它是做什么用的……可能是被它使用的 您的验证框架)。

  2. 修改业务逻辑(newAssigments)被持久化 像这样:

    parent.getAssignments().addAll(newAssignments); parent.getAssignments().forEach(assignment -&gt; assignment.setParent(parent)); parentRepository.merge(parent); // This should cascade and persist the new assigments!

【讨论】:

  • 你是对的 - 问题都在验证中。经过一些改动后一切正常!
猜你喜欢
  • 2020-10-18
  • 1970-01-01
  • 2012-07-13
  • 2015-10-29
  • 1970-01-01
  • 2015-05-22
  • 2016-05-17
  • 2013-03-29
  • 2020-05-20
相关资源
最近更新 更多