8

我是 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> {};

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

现在,第 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);
}

不过,这一次新来的孩子从来没有坚持过!

我已经尝试了 CascadeType、@GeneratedValue GenerationType、@Transactional Propagation 类型的几乎所有变体......

通过休眠(痛苦!)跟踪这一点,这就是我发现的:

  • 第二次保存时,问题出在第二个(新)孩子身上。
  • 问题似乎是,当需要持久化父级的子级列表时,新子级不在 EntityManager 中(尚未),因此被认为是瞬态的。
  • 结果,它被有效地作为 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)
...
  • 可能相关的是,在我的实际代码中,“Child”也有一个子实体的地图。这个“值”是由于“瞬态”盗用而作为空值传递的。
  • 我一直在使用 repository.saveAndFlush() 来保持同步以进行调试。当我只使用 .save() 时,我的 @PreUpdate EntityListener 被调用,但 @PostUpdate 监听器永远不会被调用。
  • 如果 Child 至少在坚持 Parent 之前被坚持或给定一个 Id,似乎不会有问题。但手动执行此操作似乎也会适得其反。不过,这是我能想到的唯一选择。

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

4

2 回答 2

5

我发现了问题,虽然我还没有一个完整的解决方案。

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

所以问题似乎是在 House 交易完成之前完成的。只需在 House 活动完成之前退出 Parent/Child 活动,所有问题都消失了。

我能想到的唯一一件事(我会更深入地挖掘)是,由于 House 在那一刻还没有完全持久化,它会导致上面描述的瞬态条件。

更新:

把一个归结为无知。显然 EntityCallback 方法“不应调用 EntityManager 或 Query 方法,并且不应访问任何其他实体对象。” 不知道。现在提出了一个问题,即我应该如何在另一个人的活动中触发实体的创建。但如有必要,我会为此启动另一个线程。谢谢大家!

于 2013-02-26T23:52:51.370 回答
3

你所展示的一切似乎都很正常,所以问题可能出在你提到的那张地图上。我在 Github 上有一个使用 Spring Data JPA 的双向一对多的工作示例。您可以查看代码或克隆并使用以下命令运行它:

git clone git://github.com/zzantozz/testbed tmp
cd tmp/spring-data
mvn -q compile exec:java -D exec.mainClass=rds.springdata.JpaBiDiOneToManyExample
于 2013-02-26T04:42:12.637 回答