jpa EntityManager的使用

一、問題記錄

最近需要配置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實例都是不同的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章