繁体   English   中英

使用JPA / Hibernate保留大型对象图时发生异常

[英]Exception while persisting a large object graph using JPA/Hibernate

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

因此,我编写了一个“脚本”,将旧数据库转换为一组JPA实体,然后保存它们。 它是这样的:

  1. 根据域模型的依赖关系创建转换顺序
  2. 对于每个实体
    1. 对旧版数据库执行数据库查询
    2. 将每个获取的表行的新对象存储在内存中的列表中
  3. 以与转换相同的顺序遍历生成的列表,并保留每个实体。

现在,前两个步骤运行良好。 坚持之后,我得到了一个例外。 当一个实体与另一实体有关系时,会发生例外。 例如,如果我们的实体之一是Book而另一个实体是Chapter定义与Book@ManyToOne(optional=false)关系。 坚持本章后,将引发异常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

当然,这表明书的状态出了点问题:似乎书没有被设置或尚未被保存。 但是,我可以验证在Chapter的转换中正确设置了Book ,并且我还可以验证在持久化Chapter类型的实体之前EntityManager可以持久存储Book类型的所有实体。 显然,我的JPA提供程序的行为不符合预期,并且由于某些原因并未真正保留我的Book对象。

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

编辑1:一些其他信息:我再次验证了在各章上调用entityManager.persist() 之前,在每个书对象上都调用了entityManager.persist()。 但是,book对象的id仍然为null,这意味着它没有正确保留。 即使不使用事务,数据库也保持为空。

编辑2:因为从上面的文字中我不清楚,所以:书和章的故事只是一个例子 任何引用另一个实体的实体都会发生这种情况。 这就好像我没有正确使用JPA / Hibernate一样,而不是没有正确设置实体的值。

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

我曾经在冬眠时遇到这样的错误。 事实证明,导致问题的原因是对象图中的圆圈和级联设置的组合。

已经有一段时间了,因此可能无法完全解决问题,但可能足以跟踪您的问题:

  1. Hibernate要插入章节 意识到它需要先插入
  2. 要插入 意识到它需要首先插入另一个实体(例如Publisher
  3. 插入发布者并执行在发布上定义的级联(例如authors
  4. 作者参考了他的lastestBook 由于hibernate在内部已经将该书标记为已处理(在第2步中),因此您不会看到任何异常,表明author.book引用了一个临时实例。

要找出这是否是您的问题,您可以启用完整的休眠调试,并遵循休眠在对象图中采用的路径。

通过与user1888440的讨论,我找到了答案。

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

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

相反,我在以编程方式定义的Spring事务 (第11.6节)中执行了此转换所涉及的所有数据库工作。 现在一切正常。

检查hbm文件中主键/对象ID的未保存值。如果您已通过hibernate框架自动创建ID,并且将ID放置在某处,则将抛出此错误。通过取消选中未保存的值是0,如果将ID设置为0,您会看到此错误。

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM