Spring @Transactional聲明式事務管理 getCurrentSessionhtml
在Spring @Transactional聲明式事務管理的配置中,hibernate.current_session_context_class=thread…java
這一句是不能加的…加了就會出錯..那爲何不能加呢?spring
那是由於在Spring事務管理中,current Session是綁定到SpringSessionContext中的,而不是ThreadLocalSessionContext中的數據庫
先結合bernate4.0說說:編程
從開 始,SessionFactory.getCurrentSession()的後臺實現是可拔插的。所以,咱們引入了新的擴展接口 (org.hibernate.context.spi.CurrentSessionContext)和session
新的配置參數(hibernate.current_session_context_class),以便對什麼是「當前session」的範圍和上下文(scope and context)的定義進行拔插。app
它定義 了單一的方法,currentSession(),特定的實現用它來負責跟蹤當前的上下文session。ui
這個接口僅有一個方法:this
SessioncurrentSession()spa
throws HibernateException
Retrieve thecurrent session according to the scoping defined by this implementation.
currentSession()表示 根據當前CurrentSessionContext的實現及定義返回」當前的Session」
這個接口…Hibernate中有3個類實現了這個接口
All Known Implementing Classes:
JTASessionContext, ManagedSessionContext, ThreadLocalSessionContext
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()方法:
public final class SessionFactoryImpl implements SessionFactoryImplementor { . . . private final transient CurrentSessionContext currentSessionContext; . . . public Session getCurrentSession() throws HibernateException { if ( currentSessionContext == null ) { throw new HibernateException( "No CurrentSessionContext configured!" ); } return currentSessionContext.currentSession(); } . . . }
SessionFactoryImpl 的currentSessionContext屬性的實際類型就是
由hibernate.current_session_context_class決定的…
2:首先設置: hibernate.current_session_context_class= org.hibernate.context.internal.ThreadLocalSessionContext
到這一句,currentSessionContext.currentSession()跟進去
public class ThreadLocalSessionContext implements CurrentSessionContext { . . . private static final ThreadLocal<Map> context = newThreadLocal<Map>(); . . . //打開一個」事務提交後自動關閉」的Session protected Session buildOrObtainSession() { return factory.withOptions() .autoClose( isAutoCloseEnabled() ) .connectionReleaseMode( getConnectionReleaseMode() ) .flushBeforeCompletion( isAutoFlushEnabled() ) .openSession(); } public final Session currentSession() throws HibernateException { //從線程局部量context中嘗試取出已經綁定到線程的Session Session current = existingSession( factory ); //若是沒有綁定到線程的Session if (current == null) { //打開一個」事務提交後自動關閉」的Session current = buildOrObtainSession(); current.getTransaction().registerSynchronization(buildCleanupSynch() ); // wrap the session in thetransaction-protection proxy if ( needsWrapping( current ) ) { current = wrap( current ); } //將獲得的Session綁定到線程中:即以<SessionFactory,Session>鍵值對方式設置到線程局部量context doBind( current, factory ); } return current; } . . . }
如今對於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
Public UserService { @Transactional public void addUser(User user) throws Exception { Session session = sessionFactory.getCurrentSession(); session.save(user); } }
由於加入了@Transactional,執行addUser()方法時,Spring的TransactionManager會自動Open Sesion,自動開啓事務,而且將此Sesion綁定到SpringSessionContext(其實是TransactionSynchronizationManager的ThreadLocal的Map)中..
而後到SessionFactoryImpl.getCurrentSesssion()的currentSessionContext.currentSession()這一句,跟進去
public class SpringSessionContext implements CurrentSessionContext { private final SessionFactoryImplementor sessionFactory; - - - - - - public Session currentSession() throws HibernateException { //關鍵就是這一句,Spring實際上會去TransactionSynchronizationManager裏查找」currentSession」 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 (TransactionSynchronizationManager.isSynchronizationActive()&& !sessionHolder.isSynchronizedWithTransaction()) { TransactionSynchronizationManager.registerSynchronization( new SpringSessionSynchronization(sessionHolder, this.sessionFactory)); sessionHolder.setSynchronizedWithTransaction(true); FlushMode flushMode = session.getFlushMode(); if (FlushMode.isManualFlushMode(flushMode)&& !TransactionSynchronizationManager.isCurrentTransactionReadOnly()){ session.setFlushMode(FlushMode.AUTO); sessionHolder.setPreviousFlushMode(flushMode); } } return session; } else if (this.jtaSessionContext != null) { Session session = this.jtaSessionContext.currentSession(); if (TransactionSynchronizationManager.isSynchronizationActive()){ TransactionSynchronizationManager.registerSynchronization(newSpringFlushSynchronization(session)); } return session; } else { throw new HibernateException("No Session found for current thread"); } } }
Object value =TransactionSynchronizationManager.getResource(this.sessionFactory); 關鍵是這一句,跟進去:
public abstract class TransactionSynchronizationManager { . . . private static final ThreadLocal<Map<Object, Object>> resources; public static Object getResource(Object key) { Object actualKey = TransactionSynchronizationUtils.unwrapResourceIfNecessary(key); //在ThreadLocal的屬性resources裏查找Session, resources裏以<SessionFactory,SessionHolder>或 <SessionFactory,Session>的鍵值對存放到ThreadLocal的Map中 Object value = doGetResource(actualKey); if (value != null && logger.isTraceEnabled()) { logger.trace("Retrievedvalue [" + value + "] for key [" + actualKey + "] bound to thread [" + Thread.currentThread().getName() + "]"); } return value; } . .. }
如今對於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