0

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

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

  1. 根据领域模型的依赖关系创建转换顺序
  2. 对于每个实体
    1. 对遗留数据库执行数据库查询
    2. 将每个获得的表行的新对象存储在内存中的列表中
  3. 以与转换相同的顺序迭代生成的列表,并持久化每个实体。

现在,前两个步骤运行良好。坚持,但是我得到一个例外。当一个实体与另一个实体有关系时会发生异常。例如,如果我们的一个实体是 a Book,而另一个实体将Chapter定义与 的@ManyToOne(optional=false)关系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 之前,所有类型的实体都Chapter被持久化了。显然,我的 JPA 提供程序没有按预期运行,并且由于某种原因并没有真正持久化我的Book对象。

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

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

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

编辑 3:核心问题似乎是,尽管正确地坚持 Book,拥有所有正确的注释, book.getId() 仍然为空。基本上,Hibernate 并没有在我的实体上设置 ids 在持久化它们之后,当我以后需要使用这些实体时会导致问题。

4

4 回答 4

3

我曾经与 hibernate 自己的这样一个错误作斗争。原来是对象图中的一个圆圈和导致问题的级联设置的组合。

已经有一段时间了,因此捕鸟可能不是 100% 准确,但也许它足以跟踪您的问题:

  1. Hibernate 想要插入章节。意识到它需要先插入
  2. 想插。意识到它需要先插入另一个实体(例如publisher
  3. 插入发布者并执行发布者定义的级联(例如作者
  4. 作者有例如参考他的lastestBook。因为休眠在内部已经将该书标记为已处理(在步骤 2 中),所以您不会收到异常说明 author.book 引用了一个瞬态实例。

要确定这是否是您的问题,您可以启用完整的休眠调试并遵循休眠通过您的对象图所采用的路径。

于 2013-01-19T18:32:21.817 回答
0

感谢与 user1888440 的讨论,我找到了答案。

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

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

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

于 2013-01-19T21:42:37.317 回答
0

检查 hbm 文件中您的主键/对象 ID 的未保存值。如果您通过休眠框架自动创建 ID,并且您在某处设置 ID,它会抛出此错误。默认情况下,未保存值是 0 ,所以如果您将 ID 设置为 0 你会看到这个错误。

于 2013-06-08T19:48:08.920 回答
-1

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

于 2013-01-19T16:47:16.497 回答