一、問題記錄
最近需要配置jpa多數據源,按照網上的方法,配置config類,裏面有個EntityManager方法
/**
* 配置EntityManager
*
* @param builder
* @return
*/
@Primary
@Bean(name = "smartlandEntityManager")
public EntityManager smartlandEntityManager(EntityManagerFactoryBuilder builder) {
return smartlandEntityManagerFactory(builder).getObject().createEntityManager();
}
使用時我是這樣寫
@Resource(name = "smartlandEntityManager")
private EntityManager entityManager;
結果發現查詢結果一直有緩存,後面查找了資料想了下,發現這麼寫有幾個問題:
1、EntityManager 是線程不安全的,不應該創建這種單例的bean去管理
2、jpa的一級緩存是針對於EntityManager,即同個EntityManager實例,如果不進行事務提交,那麼查詢的結果會有緩存,而我們平時使用@transaction開啓事務,實際上開啓的事務是註冊到spring容器上下文創建的EntityManager實例(下面會說明),並不是我在config類中創建的EntityManager實例,所以使用config類中的EntityManager實例去查詢數據會一直有緩存。
二、正確的使用方式
不需要在config類中創建EntityManager的Bean方法,直接使用
@PersistenceContext(unitName = "smartlandPersistenceUnit")
private EntityManager entityManager;
三、簡單說明
- 若我們正常使用jpa的JpaRepository,你可以看到一次完整的request請求會有兩個entityManager。第一個EntityManager由OpenEntityManagerInViewInterceptor在請求到達時創建,跟請求線程綁定,主要用於處理jpa的懶加載,第二個EntityManager是在進入Dao層時創建,用於真正的連接操作數據庫;退出Dao層後,第二個EntityManager會把那些需要懶加載的數據放到第一個EntityManager,然後第二個EntityManager關閉。
- 若是採用@resource的方式使用EntityManage,如下圖可以明顯看出實際查詢使用的entityManager和事務提交到的entityManager不是同一個實例對象,因此會出現我遇到的緩存問題。
- 若採用@PersistenceContext的方式使用entityManager,得到的實際上是一個共享的entityManager代理對象,他會在真正要調用entityManager時,從容器上下文獲取entityManager實例(獲取方法見PersistenceElement類)
- 這個entityManager綁定當前請求線程的,用如下圖可以看到實際查詢使用的和事務提交的是同一個entityManager,而且每次請求的entityManager實例都是不同的。