1. 程式人生 > >OpenSessionInView vs Dynamic Routing DataSource

OpenSessionInView vs Dynamic Routing DataSource

懶加載 acl mit 所有 spring 指標 for循環 事物 開始

  開發中有這樣的業務場景:有25家法院,法院數據庫結構都是一樣的,需要遍歷25家法院統計每家法院的指標信息。我們項目使用的是spring boot2,對於遍歷25家法院執行同樣的操作,考慮使用spring自帶的AbstractRoutingDataSource來實現數據源的切換(有關AbstractRoutingRoutingDataSource實現多數據源切換網上有很多實例,後面我也自己總結一篇)。大概的實現方式是預先定義好n個DataSource,放入一個map中,在運行中,使用一個線程安全的ThreadLoca<String>l變量來保存map的一個key值,每次訪問數據庫都先通過key值獲取對應的DataSource,通過手動切換key值,就能實現動態數據源的切換了。

  在項目中,我們數據訪問層使用sring的Jpa實現,然後在service層for循環中切換數據源,啟用了spring的事務管理器。但實際使用中發現,跑main函數的一些定時計算任務可以正常切換數據源,遍歷25家法院。但部署為web服務器,通過request請求執行service層接口時,獲取的數據始終只是一家的,並沒有成功遍歷25家。查看ThreadLoacl<String>中保存的key,確實是切換了;在service層使用@Autowired註入DataSource,調用getConnection()也能得到正確的數據庫連接,但實際執行中就是拿不到正確的數據。數據層接口繼承JpaRepository,所有也能很難看到底層jdbc連接的實際情況。

  一開始懷疑是事物沒有提交,因為同事提醒,只有確保方法執行完commit了事務,才能正常切換數據源。我們把for循環體提取出來單獨作為一個public方法,並註解了@Transaction(propagaion=Propagation.REQUIRS_NEW) 也沒有成功。最後一位大佬來說hibernate OpenSessionInView的問題。同時我們發現AbstractRoutingDataSource中的determineCurrentLookupKey()方法(在AbstractRoutingDataSource獲取真正的DataSource的時候獲取當前的key值的方法)並沒有被調用。
 OpenSessionInView網上搜說是通過filter將session和request請求的線程綁定,把事務的關閉操作放到一次請求結尾,這樣能避免懶加載中,session在dao層被提前關閉,而導致的無法獲取懶加載的數據的問題。在springboot中,這個配置是默認開啟的,但問題就在這裏,因為session被綁定到線程,導致雖然在ThreadLocal<String>變量中改變了key值,但在實際執行數據庫連接的時候會直接使用線程綁定的session中的連接,我猜測底層數據獲取的connection並沒有被更新,所以導致了數據源表面上切換成功,但實際拿不到正確數據的現象。但OpenSessionInVIew源碼裏面確實是看到緩存了Session,但為何session就不會再去取新的connection了?這個問題估計和hibernate的session設計有關,需要再看看。
  總之,OpenSessionInView這個默認為true的配置之前一直沒有註意到,但網上很多人都在說是個反人類的設置,建議默認設置該為false。
  最後在application.yml中增加spring.jpa.open-in-view: false 問題解決~

OpenSessionInView vs Dynamic Routing DataSource