5个常见的Hibernate异常及其解决方法
了解如何使用Hibernate輕松解決最常見的問題
Hibernate可能是市場上最受歡迎的JPA實現,您可以在許多地方看到它,例如:
- 您自己使用過的項目數,
- 需要Hibernate經驗的職位數量,當然還有
- 互聯網上發布的問題和例外數量
在Takipi,重點是查找和修復異常。 因此,我將重點關注列表中的最后一點,并與您分享我可能已經解決,解釋,寫博客和抱怨的5個Hibernate異常,這是我與Hibernate合作超過15年以來最多的。
盡管他們并沒有為十大例外類型提供支持 ,但谷歌的快速搜索告訴我,我并不是唯一面對這些問題的人。
但是在我們探討不同的例外之前,這篇文章是一篇很長的文章,我在免費備忘單中總結了最重要的觀點。 您可以在這篇文章的末尾下載它。
1. LazyInitializationException
如果您嘗試在沒有活動會話的情況下嘗試訪問另一個實體的未初始化關系,則Hibernate會拋出LazyInitializationException。 您可以在以下代碼片段中看到一個簡單的示例。
EntityManager em = emf.createEntityManager(); em.getTransaction().begin();Author a = em.find(Author.class, 1L);em.getTransaction().commit(); em.close();log.info(a.getFirstName() + " " + a.getLastName() + " wrote "+a.getBooks().size() + " books.");好的,您現在可能會說您永遠不會那樣做。 盡管您可能永遠不會在應用程序中使用完全相同的代碼是正確的,但您可以無意間輕松地執行相同的操作。
最受歡迎的方法是在您的業務層中未初始化的演示層中訪問與FetchType.LAZY的關系。 您可以在受歡迎的論壇中找到很多此類問題,并提出了許多不良的解決方法。
請不要在視圖反模式中使用打開的會話。 它造成的危害更大,然后才帶來收益。
修復LazyInitializationException的最佳方法是在業務層中初始化所需的關系。 但是不要僅僅因為可能有一個客戶需要其中之一就初始化所有關系。 出于性能原因,您應該只初始化所需的關系。
JPA和Hibernate提供了不同的選項來初始化延遲獲取的關系 。 我個人最喜歡的是@NamedEntityGraph ,它提供了獨立于查詢的方式來定義將隨查詢獲取的實體圖。
您可以在以下代碼段中看到一個簡單圖形的示例。 它獲取一個Author實體的Book關系。
@NamedEntityGraph(name = "graph.AuthorBooks", attributeNodes = @NamedAttributeNode("books"))您可以在Hibernate可用的任何文件中定義@NamedEntityGraph。 我更喜歡在打算與之一起使用的實體上進行操作。
如您所見,定義圖形不需要做太多的事情。 您只需要提供名稱和@NamedAttributeNode批注的數組即可定義Hibernate從數據庫中獲取的屬性。 在此示例中,只有book屬性將關系映射到Book實體。
然后,您可以提供此圖作為Hibernate的提示,以定義應使用給定查詢初始化的關系。 您可以在以下代碼段中看到一個示例。
EntityManager em = emf.createEntityManager(); em.getTransaction().begin();EntityGraph<?> graph = em.getEntityGraph("graph.AuthorBooks"); HashMap<String, Object> properties = new HashMap<>(); properties.put("javax.persistence.fetchgraph", graph);Author a = em.find(Author.class, 1L, properties);em.getTransaction().commit(); em.close();log.info(a.getFirstName() + " " + a.getLastName() + " wrote "+a.getBooks().size() + " books.");如您所見,我首先在EntityManager上調用getEntityGraph(String name)方法以獲取實體圖的實例。 在下一步中,我將創建帶有查詢提示的HashMap并將該圖添加為javax.persistence.fetchgraph。
在最后一步中,我將查詢提示作為find方法的附加參數。 這告訴Hibernate初始化與Book實體的關系,并且我可以在沒有活動的Hibernate會話的情況下調用getBooks()方法。
2. OptimisticLockException
另一個非常常見的異常是OptimisticLockException。 當您使用樂觀鎖定并檢測到實體的更新沖突時,Hibernate會拋出該錯誤。 發生這種情況最常見的原因有兩個:
在下面的代碼片段中,您可以看到具有2個并發更新的測試用例。
// EntityManager and transaction 1 EntityManager em = emf.createEntityManager(); em.getTransaction().begin();// EntityManager and transaction 2 EntityManager em2 = emf.createEntityManager(); em2.getTransaction().begin();// update 1 Author a = em.find(Author.class, 1L); a.setFirstName("changed");// update 2 Author a2 = em2.find(Author.class, 1L); a2.setFirstName("changed");// commit transaction 1 em.getTransaction().commit(); em.close();// commit transaction 2 try {em2.getTransaction().commit();Assert.fail();} catch (RollbackException e) {Assert.assertTrue(e.getCause() instanceof OptimisticLockException);log.info("2nd transaction failed with an OptimisticLockException");}em2.close();如您所見,我使用兩個獨立的EntityManager,并使用它們兩個啟動事務,獲取ID為1的Author實體,并更新名字屬性。
在我嘗試提交第二個事務并為該Author實體的并發更新進行Hibernate檢查之前,該方法可以正常工作。 當然,在實際應用中,這將通過兩次并行調用同一方法來完成。
如果使用Takipi ,則可以在發生異常時查看所有變量的狀態,這對于識別第二個更新調用的來源很有用。
Takipi的錯誤分析屏幕
在不引入悲觀鎖定的情況下,您無法做很多事情來避免這種異常,這會犧牲您的應用程序的性能。 只需嘗試盡可能頻繁地更新客戶端中的實體表示,并保持更新操作越短越好。 那應該避免大多數不必要的OptimisticLockException,并且您需要在客戶端應用程序中處理其余的剩余部分。
但是,如果只有一個用戶自己導致OptimisticLockException,那么您會發現一個可以輕松修復的錯誤。 如果您使用樂觀鎖定,則Hibernate將使用version列來跟蹤實體的當前版本并防止并發修改。 因此,您需要確保在用戶觸發實體上的任何更改后,客戶端始終更新其對實體的表示。 而且,您的客戶端應用程序也不應緩存實體或代表它的任何值對象。
3. org.hibernate.AnnotationException:未知的Id.generator
這是由錯誤的實體映射引起的,在開發過程中可能會遇到它。 原因很簡單,您可以在@GeneratedValue批注中引用未知的序列生成器,如下面的代碼片段所示。
@Id @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "authorSequence") @Column(name = "id", updatable = false, nullable = false) private Long id;@GeneratedValue批注允許您定義主鍵值的生成策略。 在前面的代碼片段中,我想使用數據庫序列,并提供“ authorSequence”作為生成器的名稱。
現在,許多開發人員期望“ authorSequence”將成為Hibernate將使用的數據庫序列的名稱。 事實并非如此。 這是@SequenceGenerator的名稱,可用于提供有關Hibernate將使用的數據庫序列的更多信息。
但是@SequenceGenerator的定義丟失了,因此Hibernate引發了AnnotationException。 要解決此問題,您必須像在以下代碼片段中一樣添加一個@SequenceGenerator批注。
@Id @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "authorSequence") @SequenceGenerator(name = "authorSequence", sequenceName = "author_seq", initialValue = 1000) @Column(name = "id", updatable = false, nullable = false) private Long id;@SequenceGenerator批注允許您提供有關數據庫序列以及Hibernate如何使用它的更多信息。 在此代碼段中,我設置了序列的名稱,即“ author_seq”,并將其初始值設置為1000。
您還可以指定序列所屬的數據庫模式以及Hibernate可以用于性能優化的分配大小。 您可以在以下文章中了解有關ID生成器的更多信息 。
4. QuerySyntaxException:表未映射
這是另一個典型的映射錯誤。 在大多數項目中,數據庫架構已經存在或獨立于您的實體映射定義。 那是一件好事。 請正確設計數據庫模式,不要讓Hibernate為您生成它!
如果您希望Hibernate在啟動時設置數據庫,最好提供一個SQL腳本,而不是讓Hibernate根據您的實體映射生成數據庫架構。
現在,回到QuerySyntaxException。 如果數據庫模式是獨立于您的實體定義的,則通常會遇到以下情況:默認表名與現有表的名稱不匹配,或者該表是其他數據庫模式的一部分。
在這種情況下,可以為架構和表名提供@Table批注,如以下代碼片段所示。
@Entity @Table(name = "author", schema = "bookstore") public class Author implements Serializable {… }5. org.hibernate.PersistentObjectException:分離的實體傳遞給持久化
此列表中的最后一個異常可能有多種原因,并且都是錯誤:
第一個很容易修復,不提供主鍵值或刪除主鍵生成策略。
僅當您自己管理主鍵值并且您的算法創建重復項時,才會發生第二種情況。 解決此問題的首選方法是讓Hibernate使用數據庫序列生成主鍵值,而不是實現自己的算法。
這并非總是可能的,在這種情況下,您必須測試和調試用于生成主鍵值的算法。 根據算法的不同,這可能是一項繁瑣且耗時的任務。
第三種經常發生在您在客戶端中使用實體時,客戶端調用了錯誤的服務器方法,該方法將保留新實體而不是更新現有實體。 解決此錯誤的明顯方法是修復客戶端中的呼叫。
另外,您可以在服務器端執行某些操作來避免此類問題,例如使用特定的值對象創建用例,而不是在同一服務器方法中處理創建和更新用例。 這使客戶開發人員更容易找到并調用正確的方法,并避免了此類問題。
摘要和備忘單
這些是我最常遇到的5個Hibernate Exception,以及如何修復它們。 如您所見,異常及其原因非常不同。 其中一些僅在開發期間發生,而另一些會在生產中對您造成打擊。 因此,最好提防并確保您熟悉這些問題。 為了讓您更輕松,我準備了一份備忘單,解釋了本文中提到的5個例外 。
翻譯自: https://www.javacodegeeks.com/2016/06/5-common-hibernate-exceptions-fix.html
創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎總結
以上是生活随笔為你收集整理的5个常见的Hibernate异常及其解决方法的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: eclipse使用技巧_有效使用Ecli
- 下一篇: 宝马将投资 6 亿英镑在英国工厂生产 M