SpringBoot Data Jpa中@OneToMany的Lazy加載問題

  1. 踩坑紀錄

踩坑紀錄

使用SpringBoot data JPA經由Junit查詢@OneToMany數據時報錯,錯誤碼如下:

org.hibernate.LazyInitializationException: could not initialize proxy [com.msystem.entity.Good#1] - no Session

解決方案:

經查詢發現,這個原因是不同的Mapping Annotation在資料庫select資料的時機是不一樣的,有些是預設使用FetchType.EAGER,代表我們索取A資料表的時候Framework就立刻幫我們把對應的外來B資料表也一起select。而有些Annotation預設是使用FetchType.LAZY,只有當我們實際呼叫getXXX()方法的時候Framework才去資料庫讀取資料。而問題出在設定為LAZY時,我們的程式要去讀取資料時資料庫連線已經關閉(或是被收回去給其他地方使用),沒辦法延遲取得資料。

方式一【推薦】:使用@Transactional標註在會遇到此問題的Method上。

利用資料庫的交易(Transaction)機制,搭配Spring Framework的@Transactional註釋可以解決這個問題,Spring讓程式從進入Method到離開Method都確保其獨立完整性。

方式二:在Entity Class中使用@OneToMany(fetch = FetchType.EAGER),讓JPA立刻索取資料。

這種方式可以解決要讀取資料時連線已經關閉的問題,但會帶來效能問題。如果對應的外來資料表的資料非常多的話,每次都必須大量的讀取不一定會用上的資料。但是開發者確定資料量不多、或是每次都會用上這些資料的話,將Fetch Type改為EAGER也是OK的。

在解決問題的時候,發現在Junit測試方法的時候標註@Transactional可以正常查出數據,但若只在方法上面標註@Transactional時,使用Junit仍會報錯。研究後發現在Junit中,原本方法標註的@Transactional會失效,以下擴展一下@Transactional的知識
@Transactional 失效的場景:

  1. 在Junit中,原本方法標註的@Transactional會失效
  2. Transactional 注解標注方法修飾符為非 public 時,@Transactional 注解將會不起作用
  3. 在類內部調用調用類內部 @Transactional 標注的方法
  4. 事務方法內部捕捉了異常,沒有拋出新的異常,導致事務操作不會進行回滾

轉載請注明來源,歡迎對文章中的引用來源進行考證,歡迎指出任何有錯誤或不夠清晰的表達。可以郵件至 b8954008@gmail.com