Spring @Transactional 聲明式事務管理 getCurrentSession

轉載於:http://blog.csdn.net/irelandken/article/details/7193123

Spring @Transactional聲明式事務管理  getCurrentSession

 

在Spring @Transactional聲明式事務管理的配置中,hibernate.current_session_context_class=thread…

這一句是不能加的…加了就會出錯..那爲什麼不能加呢?

那是因爲在Spring事務管理中,current Session是綁定到SpringSessionContext中的,而不是ThreadLocalSessionContext中的

 

先結合bernate4.0說說:

從開 始,SessionFactory.getCurrentSession()的後臺實現是可拔插的。因此,我們引入了新的擴展接口 (org.hibernate.context.spi.CurrentSessionContext)和

新的配置參數(hibernate.current_session_context_class),以便對什麼是“當前session”的範圍和上下文(scope and context)的定義進行拔插。

 

它定義 了單一的方法,currentSession(),特定的實現用它來負責跟蹤當前的上下文session。

 

首先我們看看org.hibernate.context.spi.CurrentSessionContext

這個接口僅有一個方法:

SessioncurrentSession()

                       throws HibernateException

Retrieve thecurrent session according to the scoping defined by this implementation.

 

currentSession()表示 根據當前CurrentSessionContext的實現及定義返回”當前的Session”

 

這個接口…Hibernate中有3個類實現了這個接口

All Known Implementing Classes:

JTASessionContextManagedSessionContextThreadLocalSessionContext

 

1: org.hibernate.context.internal.ThreadLocalSessionContext - 當前session通過當前執行的線程來跟蹤和界定。

 

2: org.hibernate.context.internal.JTASessionContext- 當前session根據JTA來跟蹤和界定。這和以前的僅支持JTA的方法是完全一樣的。

 

3: org.hibernate.context.internal.ManagedSessionContext..

 

Spring爲事務管理,也實現了此接口:

1: org.springframework.orm.hibernate4.SpringSessionContext– 當前Session根據Spring和事務管理器來跟蹤和界定.

 

 

這幾種實現都提供了“每數據庫事務對應一個session”的編程模型,也稱作每次請求一個session。Hibernate session的起始和終結由數據庫事務的生存來控制。

 

 

hibernate.current_session_context_class 配置參數定義了應該採用哪個org.hibernate.context.spi.CurrentSessionContext實現。

 

一般而言,此參數的值指明瞭要使用的實 現類的全名,但那兩個內置的實現可以使用簡寫,即"jta"和"thread"。

 

hibernate.current_session_context_class=thread

實質是:

hibernate.current_session_context_class= org.hibernate.context.internal.ThreadLocalSessionContext

 

同理:

hibernate.current_session_context_class=jta

實質是:

hibernate.current_session_context_class= org.hibernate.context.internal.JTASessionContext

 

 

 

而在Spring @Transactional聲明式事務管理,”currentSession”的定義爲: 當前被 Spring事務管理器 管理的Session,此時應配置:

hibernate.current_session_context_class=org.springframework.orm.hibernate4.SpringSessionContext

 

 

spring 整合hibernate管理事務後,由Spring的TransactionManager管理事務後, currentSession是綁定到SpringSessionContext的,而不是thread。

此時hibernate.current_session_context_class應該是SpringSessionContext,而它又會在使用LocalSessionFactoryBean時自動的設置。

所以就不需要你去設置current_session_context_class

 

-   -       - --         -

下面我們來分析一下SessionFactoryImpl, org.hibernate.context.spi.CurrentSessionContext

org.hibernate.context.internal.ThreadLocalSessionContext

org.springframework.orm.hibernate4.SpringSessionContext

這些類的源代碼

 

1: 分析sessionFactory.getCurrentSession() 我們跟進去

來到SessionFactoryImpl.getCurrentSession()方法:

 

[java] view plaincopy
  1. public final class SessionFactoryImpl  
  2.       implements SessionFactoryImplementor {  
  3.   . . .  
  4.   private final transient CurrentSessionContext currentSessionContext;  
  5.   . . .  
  6.   public Session getCurrentSession() throws HibernateException {  
  7.       if ( currentSessionContext == null ) {  
  8.          throw new HibernateException( "No CurrentSessionContext configured!" );  
  9.       }  
  10.       return currentSessionContext.currentSession();  
  11.   }  
  12.    
  13. . . .  
  14. }  


 

SessionFactoryImpl 的currentSessionContext屬性的實際類型就是

由hibernate.current_session_context_class決定的…

 

2:首先設置: hibernate.current_session_context_class= org.hibernate.context.internal.ThreadLocalSessionContext

   到這一句,currentSessionContext.currentSession()跟進去

 

[java] view plaincopy
  1. public class ThreadLocalSessionContext implements CurrentSessionContext {  
  2.     . . .  
  3.    private static final ThreadLocal<Map> context = newThreadLocal<Map>();  
  4.    . . .  
  5.    
  6.    //打開一個”事務提交後自動關閉”的Session  
  7.    protected Session buildOrObtainSession() {  
  8.        return factory.withOptions()  
  9.             .autoClose( isAutoCloseEnabled() )  
  10.             .connectionReleaseMode( getConnectionReleaseMode() )  
  11.             .flushBeforeCompletion( isAutoFlushEnabled() )  
  12.             .openSession();  
  13.     }  
  14.    
  15.     public final Session currentSession() throws HibernateException {  
  16.       //從線程局部量context中嘗試取出已經綁定到線程的Session  
  17.       Session current = existingSession( factory );  
  18.        
  19.       //如果沒有綁定到線程的Session  
  20.       if (current == null) {  
  21.          //打開一個”事務提交後自動關閉”的Session  
  22.          current = buildOrObtainSession();  
  23.             current.getTransaction().registerSynchronization(buildCleanupSynch() );  
  24.          // wrap the session in thetransaction-protection proxy  
  25.          if ( needsWrapping( current ) ) {  
  26.             current = wrap( current );  
  27.          }  
  28.          //將得到的Session綁定到線程中:即以<SessionFactory,Session>鍵值對方式設置到線程局部量context  
  29.          doBind( current, factory );  
  30.       }  
  31.       return current;  
  32.    }  
  33. . . .  
  34. }  


 

現在對於hibernate.current_session_context_class= thread時的getCurrentSession()就很清楚了:

1:嘗試取出綁定到線程的Session

2:如果沒有,則開啓一個”事務提交後自動關閉”的Session,並將此Session加入到ThreadLocal的Map中.

3:返回Session


 

 

3:然後再分析:hibernate.current_session_context_class=org.springframework.orm.hibernate4.SpringSessionContext

 

[java] view plaincopy
  1. Public UserService  
  2. {  
  3.    @Transactional  
  4.    public void addUser(User user) throws Exception  
  5.    {  
  6.       Session session = sessionFactory.getCurrentSession();  
  7.        
  8.       session.save(user);  
  9.    }  
  10. }  


 

因爲加入了@Transactional,執行addUser()方法時,Spring的TransactionManager會自動Open Sesion,自動開啓事務,並且將此Sesion綁定到SpringSessionContext(實際上是TransactionSynchronizationManager的ThreadLocal的Map)中..

 

 

然後到SessionFactoryImpl.getCurrentSesssion()的currentSessionContext.currentSession()這一句,跟進去

 

[java] view plaincopy
  1. public class SpringSessionContext implements CurrentSessionContext {  
  2.    
  3.    private final SessionFactoryImplementor sessionFactory;  
  4.    
  5.    
  6.    -  - - - - -  
  7.    
  8.    public Session currentSession() throws HibernateException {  
  9.     //關鍵就是這一句,Spring實際上會去TransactionSynchronizationManager裏查找”currentSession”  
  10.    
  11.     Object value = TransactionSynchronizationManager.getResource(this.sessionFactory);  
  12.       if (value instanceof Session) {  
  13.          return (Session) value;  
  14.       }  
  15.       else if (value instanceof SessionHolder) {  
  16.          SessionHolder sessionHolder = (SessionHolder) value;  
  17.          Session session = sessionHolder.getSession();  
  18.          if (TransactionSynchronizationManager.isSynchronizationActive()&&  
  19.                 !sessionHolder.isSynchronizedWithTransaction()) {  
  20.             TransactionSynchronizationManager.registerSynchronization(  
  21.                    new SpringSessionSynchronization(sessionHolder, this.sessionFactory));  
  22.             sessionHolder.setSynchronizedWithTransaction(true);  
  23.     
  24.             FlushMode flushMode = session.getFlushMode();  
  25.             if (FlushMode.isManualFlushMode(flushMode)&&  
  26.                    !TransactionSynchronizationManager.isCurrentTransactionReadOnly()){  
  27.                 session.setFlushMode(FlushMode.AUTO);  
  28.                 sessionHolder.setPreviousFlushMode(flushMode);  
  29.             }  
  30.          }  
  31.          return session;  
  32.       }  
  33.       else if (this.jtaSessionContext != null) {  
  34.          Session session = this.jtaSessionContext.currentSession();  
  35.          if (TransactionSynchronizationManager.isSynchronizationActive()){  
  36.             TransactionSynchronizationManager.registerSynchronization(newSpringFlushSynchronization(session));  
  37.          }  
  38.          return session;  
  39.       }  
  40.       else {  
  41.          throw new HibernateException("No Session found for current thread");  
  42.       }  
  43.    }  
  44.    
  45. }  


 

Object value =TransactionSynchronizationManager.getResource(this.sessionFactory); 關鍵是這一句,跟進去:

 

[java] view plaincopy
  1. public abstract class TransactionSynchronizationManager {  
  2.    
  3.  . . .  
  4.  private static final ThreadLocal<Map<Object, Object>> resources;  
  5.    
  6.  public static Object getResource(Object key) {  
  7.       Object actualKey = TransactionSynchronizationUtils.unwrapResourceIfNecessary(key);  
  8.       //在ThreadLocal的屬性resources裏查找Session, resources裏以<SessionFactory,SessionHolder>或 <SessionFactory,Session>的鍵值對存放到ThreadLocal的Map中  
  9.       Object value = doGetResource(actualKey);  
  10.       if (value != null && logger.isTraceEnabled()) {  
  11.          logger.trace("Retrievedvalue [" + value + "] for key [" + actualKey + "] bound to thread [" +  
  12.                 Thread.currentThread().getName() + "]");  
  13.       }  
  14.       return value;  
  15.    }  
  16.    
  17.  . ..  
  18. }  


 

現在對於hibernate.current_session_context_class= org.springframework.orm.hibernate4.SpringSessionContext時的getCurrentSession()就很清楚了:

 

 

1: @Transactional聲明的方法執行時,Spring的TransactionManager會自動Open Sesion,自動開啓事務,並且將此Sesion綁定到SpringSessionContext(實際上是TransactionSynchronizationManager的ThreadLocal的Map)中..

 

2:SessionFactory.getCurrentSession()方法執行時,調用SpringSessionContext.currentSession()從TransactionSynchronizationManager的上下文中查找 當前的Session

 

3:找到後返回當前的Session,找不到,則返回HibernateException("No Sessionfound for current thread")



PS: 從中,我們也知道了,執行SessionFactoryImpl.openSession()時,只是簡單地new 一個SessionBuilder,然後調用SessionBuilder.openSession(),得到的Session是不會綁定到任何 org.hibernate.context.spi.CurrentSessionContext 在上下文中的.


////////////////////////////////////////////////////////////////--------------------------------------------------------------------------------------------------------------------------------------- 



總結: hibernate.current_session_context_class=thread(org.hibernate.context.internal.ThreadLocalSessionContext)

與      hibernate.current_session_context_class=org.springframework.orm.hibernate4.SpringSessionContext 時的SessionFactory.getCurrentSession()的不同之處在於: 

 前者在ThreadLocalSessionContext裏的線程局部的Map中查找Session,

 而後者在SpringSessionContext的上下文(TransactionSynchronizationManager裏的線程局部的Map)中查找...


      最終,你會發覺,無論是ThreadLocalSessionContext 或 SpringSessionContext 查找的"currentSession",都是以類似鍵值對<SessionFactory,Session>的形式存放到ThreadLocal的Map中,也就是說這兩者的上下文都是一個ThreadLocal的Map...查找時以SessionFactory爲鍵來查找對應的Session,所以在同一線程中,一個SessionFactory只能有一個currentSession


發佈了86 篇原創文章 · 獲贊 6 · 訪問量 36萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章