Spring-JPA:更新父实体无法保留新的子实体,而是将它们解释为Transient

Did*_*jit 6 spring hibernate jpa transactional

我是Spring/JPA/Hibernate的新手,虽然听起来很简单,但事实并非如此.我可以使用一些帮助.

我有一个父实体,其中包含子实体列表.我会用这些来简化讨论:

@Entity
public class Parent {
    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    private Long id;

    @OneToMany(fetch=FetchType.EAGER, cascade = CascadeType.ALL, mappedBy="parent")
    private List<Child> children= new ArrayList<Child>();

    etc...
}

@Entity
public class Child {
    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    private Long id;

    @ManyToOne
    private Parent parent;

    etc...
}

@Repository
public interface ParentRepository extends JpaRepository<Parent, Long> {};
Run Code Online (Sandbox Code Playgroud)

第1轮,我创建一个新的父级和一个新的子级,将子级添加到父级列表并在子级上设置父级.当我保存父母时,孩子也被保存.

@Transactional(propagation=Propagation.REQUIRES_NEW)
void create() {
    Parent parent = new Parent();
    Child  child  = new Child();
    parent.add(child);
    child.setParent(parent);
    parent = repository.save(parent);
}
Run Code Online (Sandbox Code Playgroud)

现在,第2轮,我添加了一个新的孩子:

@Transactional(propagation=Propagation.REQUIRES_NEW)
void update() {
    Parent parent = repository.findOne(parentID);
    Child newChild = new Child();
    newChild.setParent(parent);
    parent.add(newChild);
    parent = repository.save(parent);
}
Run Code Online (Sandbox Code Playgroud)

但是,这次新孩子永远不会坚持下去!

我已尝试过大多数CascadeType,@ GeneratedValue GenerationType,@ Transaction传播类型......

通过休眠来追踪这个(痛苦!),这是我发现的:

  • 第二次保存时,问题出在第二个(新)孩子身上.
  • 问题似乎是,当需要持久保存父级子列表时,新子级不在EntityManager中(尚未),因此被认为是Transient.
  • 因此,它实际上是作为null传递给链,导致以下结果:
org.springframework.transaction.TransactionSystemException: Could not commit JPA transaction; nested exception is
javax.persistence.RollbackException: Error while committing thetransaction
    at org.springframework.orm.jpa.JpaTransactionManager.doCommit(JpaTransactionManager.java:521)
...

Caused by: javax.persistence.RollbackException: Error while committing the transaction
    at org.hibernate.ejb.TransactionImpl.commit(TransactionImpl.java:92)
    at org.springframework.orm.jpa.JpaTransactionManager.doCommit(JpaTransactionManager.java:512)
...

Caused by: org.hibernate.AssertionFailure: collection [null] was not processed by flush()
    at org.hibernate.engine.spi.CollectionEntry.postFlush(CollectionEntry.java:225)
...
Run Code Online (Sandbox Code Playgroud)
  • 可能相关的是,在我的实际代码中,"Child"也有一个子实体的地图.由于"瞬态"盗用,这个"价值"被传递为零.
  • 我一直在使用repository.saveAndFlush()来保持调试的同步.当我只使用.save()时,我会调用@PreUpdate EntityListener,但@PostUpdate监听器永远不会被调用.
  • 似乎如果孩子只是坚持或至少在坚持父母之前获得了Id,就不会有问题.但手动执行此操作似乎也适得其反.不过,这是我能想到的唯一选择.

谢谢阅读.任何帮助将非常感激!

Did*_*jit 5

我发现了问题,尽管我还没有真正的完整解决方案。

\n\n

首先,一些额外的背景。除了“父母”和“孩子”之外,我还有一个相关的课程,我在这里称之为“House”。House 定义了一个 EntityListener,以便在保存/更新它时,会创建/更新关联的父对象和子对象。因此,正是在 House 的 PostPersist/PostUpdate 期间,Parent 和 Child 对象被创建、链接、指向 House,然后持久化。

\n\n

所以问题似乎是在房屋交易完成之前完成的。只需将“家长/孩子”活动撤出,直到“House”活动完成后,所有问题就迎刃而解了。

\n\n

我唯一能想到的(我会更深入地挖掘)是,由于 House 在那一刻尚未完全持续,因此会导致上述瞬态条件。

\n\n

更新:

\n\n

将其归因于无知。显然 EntityCallback 方法“不应调用 EntityMan\xc2\xadager 或 Query 方法,也不应访问任何其他实体对象”。 不知道。现在这就提出了一个问题:我应该如何在另一个活动上触发实体的创建。但如果有必要,我会为此启动另一个线程。谢谢大家!

\n