我们正在创建一个由JPA支持的新Web应用程序来替换旧的Web应用程序。作为迁移的一部分,我们将旧应用程序的数据库转换为新的,更复杂的JPA管理数据库。
所以我编写了一个'脚本',将旧数据库转换为一组JPA实体,然后保存它们。它的工作原理如下:
现在,前两个步骤运作良好。坚持不懈,然而我得到一个例外。当一个实体与另一个实体有关系时会发生异常。例如,如果我们的某个实体是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,导致以后需要使用这些实体时出现问题。
答案 0 :(得分:3)
我曾经与hibernate本人的这种错误作斗争。事实证明,它是对象图中的圆圈和导致问题的级联设置的组合。
已经有一段时间了,所以家禽可能不是100%准确,但也许它足以跟踪你的问题:
要确定这是否是您的问题,您可以启用完整的休眠调试,并按照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永远不会为空。