2015-04-17 34 views

回答

0

使用註釋是實現聲明式事務管理的一種方法,但不是唯一的方法。您也可以在xml配置中使用txaop名稱空間。通過這種方式,您可以使用集中式事務配置,您還可以在通配符中使用通配符。 您可以用相同的方式使用sessionFactory.getCurrentSession()。只是交易劃分風格發生了變化。

有關更多詳細信息,請參閱Spring reference documentation

10

簡單的答案是:,當然也可以作爲SessionFactory.getCurrentSession()只是一個接口的方法,這樣你可以寫你自己的實現類,讓你任何你喜歡的Session

但是,這可能不是您正在尋找的答案。

我們一直在問自己一個類似的問題:爲什麼使用Spring的事務管理Hibernate的,做我們必須添加@Transactional我們的所有方法,甚至是那些只SELECT數據,因此當不需要進行內執行數據庫事務的上下文?

答案並不那麼簡單,但讓我們看一下所涉及的一些管道,看看我們是否可以理解它。

首先,正如在SO上的其他地方所提到的,Session的想法與交易的想法有着根本的聯繫。在javadoc中提供了Session接口:

會話的生命週期由邏輯事務的開始和結束限定。 (長事務可能跨越多個數據庫事務。)

和鑽研@Transactional類的javadoc的證實,它的目的是表明,當代碼應該是「事務上下文」,這是不一定內執行交易的數據庫的上下文。

這也解釋了爲什麼Spring的@Transactional註釋允許您設置屬性readOnly=true,但稍後會更多。

再回到Spring4和Hibernate4,當你調用sessionFactory.getCurrentSession()它實際上執行SessionFactoryImpl下面的代碼:

public Session getCurrentSession() throws HibernateException { 
    if (currentSessionContext == null) { 
     throw new HibernateException("No CurrentSessionContext configured!"); 
    } 
    return currentSessionContext.currentSession(); 
} 

所以它實際上是推遲到CurrentSessionContext實現它(除非你使用JTA,你可能不想打開潘多拉的盒子)是由SpringSessionContext類處理:

@Override 
public Session currentSession() throws HibernateException { 
    Object value = TransactionSynchronizationManager.getResource(this.sessionFactory); 
    if (value instanceof Session) { 
     return (Session) value; 
    } 
    else if (value instanceof SessionHolder) { 
     SessionHolder sessionHolder = (SessionHolder) value; 
     Session session = sessionHolder.getSession(); 
     if (!sessionHolder.isSynchronizedWithTransaction() && 
       TransactionSynchronizationManager.isSynchronizationActive()) { 
      TransactionSynchronizationManager.registerSynchronization(
        new SpringSessionSynchronization(sessionHolder, this.sessionFactory, false)); 
      sessionHolder.setSynchronizedWithTransaction(true); 
      // Switch to FlushMode.AUTO, as we have to assume a thread-bound Session 
      // with FlushMode.MANUAL, which needs to allow flushing within the transaction. 
      FlushMode flushMode = session.getFlushMode(); 
      if (flushMode.equals(FlushMode.MANUAL) && 
        !TransactionSynchronizationManager.isCurrentTransactionReadOnly()) { 
       session.setFlushMode(FlushMode.AUTO); 
       sessionHolder.setPreviousFlushMode(flushMode); 
      } 
     } 
     return session; 
    } 

    if (this.transactionManager != null) { 
     try { 
      if (this.transactionManager.getStatus() == Status.STATUS_ACTIVE) { 
       Session session = this.jtaSessionContext.currentSession(); 
       if (TransactionSynchronizationManager.isSynchronizationActive()) { 
        TransactionSynchronizationManager.registerSynchronization(new SpringFlushSynchronization(session)); 
       } 
       return session; 
      } 
     } 
     catch (SystemException ex) { 
      throw new HibernateException("JTA TransactionManager found but status check failed", ex); 
     } 
    } 

    if (TransactionSynchronizationManager.isSynchronizationActive()) { 
     Session session = this.sessionFactory.openSession(); 
     if (TransactionSynchronizationManager.isCurrentTransactionReadOnly()) { 
      session.setFlushMode(FlushMode.MANUAL); 
     } 
     SessionHolder sessionHolder = new SessionHolder(session); 
     TransactionSynchronizationManager.registerSynchronization(
       new SpringSessionSynchronization(sessionHolder, this.sessionFactory, true)); 
     TransactionSynchronizationManager.bindResource(this.sessionFactory, sessionHolder); 
     sessionHolder.setSynchronizedWithTransaction(true); 
     return session; 
    } 
    else { 
     throw new HibernateException("Could not obtain transaction-synchronized Session for current thread"); 
    } 
} 

,並解釋爲什麼你會看到異常:

無法獲得交易同步會話當前線程的

當你因爲沒有@Transactional註釋切入點還沒有執行這是不符合@TransactionalTransactionSynchronizationManager.isSynchronizationActive()回報false註釋的方法調用sessionFactory.getCurrentSession()這將創建一個同步事務。 (見org.springframework.transaction.interceptor.TransactionInterceptor獲取更多信息。)

因此,這使我們回到我們的用例,這是我們不希望調用PlatformTransactionManager的開銷和它的數據庫事務代碼時,我們只需要執行一個SELECT針對數據庫。實現此目的的簡單方法是不要撥打sessionFactory.getCurrentSession(),而是直接打開Session。例如藉此Spring管理代碼:

public class MyHibernateService { 

    @Autowired 
    private SessionFactory sessionFactory; 

    protected Session transactionalSession() { 
     return sessionFactory.getCurrentSession(); 
    } 

    protected Session readOnlySession() { 
     if(TransactionSynchronizationManager.isSynchronizationActive()) 
      return transactionalSession(); 
     Session session = this.sessionFactory.openSession(); 
     session.setFlushMode(FlushMode.MANUAL); 
     return session; 
    } 

    public List<SalixUrl> activeUrls() { 
     return readOnlySession().createCriteria(SalixUrl.class) 
       .add(Restrictions.gt("published", LocalDateTime.now())) 
       .add(Restrictions.lt("removed", LocalDateTime.now())) 
       .list(); 
    } 

    @Transactional 
    public List<SalixUrl> refreshUrls() { 
     List<SalixUrl> urls = activeUrls(); 
     for(SalixUrl url : urls) { 
      url.setLastChecked(LocalDateTime.now()); 
      transactionalSession().update(url); 
     } 
    } 
} 

這將允許你打電話myHibernateService.activeUrls(),而無需一個@Transactional標註,還myHibernateService.refreshUrls()你會想經過PlatformTransactionManager

如果這段代碼看起來很熟悉,可能是因爲您已經查看了OpenSessionInViewFilter(或攔截器)的來源,它通常用於緩解LazyLoadingException s,並且還負責解決很多n + 1問題程序員認爲他們通過使用FetchType.LAZY來優化他們的ORM模型來定義實體關係,但沒有編寫他們的服務/存儲庫層來實際獲取需要被提取的視圖來生成。

無論如何,你不想使用上面的代碼。相反,你可以使用想要使用@Transactional註釋,並讓Spring和Hibernate框架決定實際需要什麼類型的數據庫事務。

如果你擔心性能,你有幾種選擇:

否1.你可以使用Spring的@Transactional(readOnly=true)但請注意,這不是一定一個偉大的想法。我不主張使用javax @Transactional,因爲它更通用 - 如果你將你的顏色綁定到Spring桅杆上,那麼你可以使用它所提供的。相反,我很警惕,因爲它所做的一切(使用當前實現)都是要求底層數據庫提供者的Connection對象被標記爲只讀。由於幾個原因,這可能是有問題的。

首先,您的數據庫提供程序可能不支持只讀連接(例如MSSQL服務器的jTDS JDBC驅動程序),因此可能是毫無意義。

第二個原因是由於連接池。如果您使用的數據庫支持只讀連接(如PostgreSQL)和連接池(如C3P0),那麼您並不想將某些連接標記爲只讀,然後將它們返回到池中,然後允許在需要執行數據庫寫入的情況下提供它們。 (我沒有用Hibernate4和Spring4測試過,但是它肯定是Spring3,Hibernate3和C3P0的問題。)

2.使用緩存。通過我們近來可以訪問的硬件,緩存是,可能是的答案,並且您有很多選項可供您使用。您可以爲Hibernate實體配置第二級緩存,並且Spring本身有一個很好的spring-cache模塊,可以緩存服務/存儲庫方法 - 查看如何整合EhCache。

3.寫你自己的數據庫查詢使用JDBC或其他。 Gavin King(Hibernate作者)一直在指出相當長的一段時間,僅僅因爲你使用Hibernate for ORM,你不需要使用它的一切:https://plus.google.com/+GavinKing/posts/LGJU1NorAvY(我不能找到明確的引用,他說「不」 t使用Hibernate的性能SELECT「但我認爲幾年前我讀了一些東西)。

但有兩個比較重要的問題:

否1.不應擔心性能。如果你需要,那麼你不應該讀這個,因爲你應該已經知道所有這些了;-)但是忽略了我的表情,不要浪費時間進行原子代碼優化,而是需要像工程師一樣行事,然後看在整個系統中(如Dirk Gently),然後對最有效的方式作出判斷,以儘可能提高您的系統系統。 請記住:協和隊不再飛行的原因有幾個。

否2.您可能不需要再使用SessionFactory了。 JPA 2和EntityManager已被設計爲明確使用SessionFactory不必要的。即使Emmanuel Bernard(另一位Hibernate作者)在幾年前給了我們這樣的建議:http://www.theserverside.com/news/2240186700/The-JPA-20-EntityManager-vs-the-Hibernate-Session-Which-one-to-use

但是你知道什麼:我喜歡SessionFactory和Hibernate Criteria API以及它的一切。所以我會繼續使用它,直到他們棄用Spring框架對它的支持。因爲,正如我所說的,如果你已經將你的顏色固定在框架上,那麼你可以使用框架提供的所有東西。實際上,抽象的主要優點(即可以將底層ORM或數據庫提供程序替換掉)是您可能不需要擔心的大概是。 (但是,我已經去過那裏了 - 我不得不將中等規模的代碼庫從MSSQL遷移到PostgreSQL,最大的問題不是Spring/ORM層,而是數據庫特定的代碼,如存儲過程和觸發器,事實上以前的開發人員試圖通過使用@Transactional(readOnly=true)來優化查詢,但不知道MSSQL實際上不支持它,並且在使用PostgreSQL和C3P0時會中斷。是的,我仍然很痛苦。)

+0

我見過的最好的答案...你能幫我解決這個問題嗎?http://stackoverflow.com/questions/38823693/java-more-than-one -DB連接功能於UserTransaction的?我一直在找到這個解決方案,但是我還沒有找到... – GMsoF

相關問題