使用JPA / Hibernate持久化大对象图时出现异常

时间:2013-01-19 16:23:33

标签: java hibernate jpa spring-orm

我们正在创建一个由JPA支持的新Web应用程序来替换旧的Web应用程序。作为迁移的一部分,我们将旧应用程序的数据库转换为新的,更复杂的JPA管理数据库。

所以我编写了一个'脚本',将旧数据库转换为一组JPA实体,然后保存它们。它的工作原理如下:

  1. 根据域模型的依赖关系创建转换顺序
  2. 对于每个实体
    1. 对旧版数据库执行数据库查询
    2. 将每个获取的表格行的新对象存储在内存列表中
  3. 按照与转换相同的顺序迭代生成的列表,并保留每个实体。
  4. 现在,前两个步骤运作良好。坚持不懈,然而我得到一个例外。当一个实体与另一个实体有关系时会发生异常。例如,如果我们的某个实体是Book,而另一个实体将Chapter定义与@ManyToOne(optional=false)的{​​{1}}关系。在持久化章节时,它会抛出异常Book

    当然,这表明该书的状态出现了问题:它似乎未设置或尚未保留。但是,我可以验证java.lang.IllegalStateException: org.hibernate.TransientPropertyValueException: Not-null property references a transient value - transient instance must be saved before current operation: models.Chapter.book -> models.Book转换中Book是否已正确设置,我还可以验证Chapter是否保留了Book类型的所有实体之前类型EntityManager的实体得到持久化。显然,我的JPA提供程序没有按预期运行,并且由于某种原因没有真正保留Chapter个对象。

    什么解决方案可以让我保存已转换为数据库的整个对象图?我使用Hibernate作为我的JPA提供程序,我还使用Spring 3.1注入依赖项和Book s。

    编辑1:一些额外的信息:我再次验证了在每个书籍对象上调用entityManager.persist()之前在章节上调用 entityManager.persist()。但是,book对象的id保持为null,这意味着它没有被正确保留。尽管没有使用交易,数据库仍然是空的。

    编辑2:因为我不认为从上面的文字中可以清楚地看到:书和章的故事只是一个例子。它发生在引用另一个实体的任何实体上。这使得我似乎没有正确使用JPA / Hibernate而不是正确设置我的实体的值。

    编辑3:核心问题似乎是尽管正确地保持Book,拥有所有正确的注释,book.getId()仍为null。基本上,Hibernate在持久化后不会在我的实体上设置id,导致以后需要使用这些实体时出现问题。

4 个答案:

答案 0 :(得分:3)

我曾经与hibernate本人的这种错误作斗争。事实证明,它是对象图中的圆圈和导致问题的级联设置的组合。

已经有一段时间了,所以家禽可能不是100%准确,但也许它足以跟踪你的问题:

  1. Hibernate想要插入章节。意识到它需要先插入
  2. 想要插入。意识到需要先插入另一个实体(例如 publisher
  3. 插入发布商并执行在发布商上定义的级联(例如 authors
  4. 作者有例如参考他的 lastestBook 。因为hibernate内部已经将本书标记为已处理(在步骤2中),所以您不会得到一个异常,说明author.book引用了一个瞬态实例。
  5. 要确定这是否是您的问题,您可以启用完整的休眠调试,并按照hibernate通过对象图的路径。

答案 1 :(得分:0)

由于我与user1888440的讨论,我找到了答案。

这个答案的解决方案是Spring @Transactional注释在我的应用程序中不起作用。这意味着Hibernate所做的一切都不会发生在事务的上下文中。这意味着Hibernate在持久化后不会设置ID,这意味着所有转换都会崩溃。

@Transactional不起作用的原因可能是因为我没有提到这个事实:这个脚本是Play 2.0(实际上是2.1)应用程序的一部分,因此使用SBT构建。 SBT不使用普通的Java设置来构建应用程序,而是使用Scala编译器来编译Java。我的猜测是Scala编译与Spring需要使@Transactional工作的AspectJ不能很好地工作。

相反,我在programmatically defined Spring transaction内执行了此转换中涉及的所有数据库工作(第11.6节)。现在一切都像预期的那样。

答案 2 :(得分:0)

检查你的hbm文件中你的主键/对象ID的未保存值。如果你有hibernate框架的自动ID创建,并且你在某个地方找到了它,它就会抛出这个错误。通过defaut,unsaved-value是0,因此,如果您将ID设置为0,您将看到此错误。

答案 3 :(得分:-1)

听起来你忘记在坚持之前为每一章分配一本书。即使您已经持久化了Book,也需要将其分配给Chapter实例的#book属性,然后才能保留Chapter。这是因为您已将关系指定为非可选关系。 #book永远不会为空。