[英]Exception while persisting a large object graph using JPA/Hibernate
我們正在創建一個由JPA支持的新Web應用程序,以替換舊的Web應用程序。 作為遷移的一部分,我們正在將舊應用程序的數據庫轉換為新的,更復雜的,由JPA管理的數據庫。
因此,我編寫了一個“腳本”,將舊數據庫轉換為一組JPA實體,然后保存它們。 它是這樣的:
現在,前兩個步驟運行良好。 堅持之后,我得到了一個例外。 當一個實體與另一實體有關系時,會發生例外。 例如,如果我們的實體之一是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,這會導致以后需要使用這些實體時出現問題。
我曾經在冬眠時遇到這樣的錯誤。 事實證明,導致問題的原因是對象圖中的圓圈和級聯設置的組合。
已經有一段時間了,因此可能無法完全解決問題,但可能足以跟蹤您的問題:
要找出這是否是您的問題,您可以啟用完整的休眠調試,並遵循休眠在對象圖中采用的路徑。
通過與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.