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。
這個介面僅有一個方法:
SessioncurrentSession()
Retrieve thecurrent session according to the scoping defined by this implementation.
currentSession()表示 根據當前CurrentSessionContext的實現及定義返回”當前的Session”
這個介面…Hibernate中有3個類實現了這個介面
All Known Implementing Classes:
1: org.hibernate.context.internal.ThreadLocalSessionContext - 當前session通過當前執行的執行緒來跟蹤和界定。
2: org.hibernate.context.internal.JTASessionContext- 當前session根據JTA來跟蹤和界定。這和以前的僅支援JTA的方法是完全一樣的。
3: org.hibernate.context.internal.ManagedSessionContext..
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,此時應配置:
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
這些類的原始碼
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
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,
最終,你會發覺,無論是ThreadLocalSessionContext 或 SpringSessionContext 查詢的"currentSession",都是以類似鍵值對<SessionFactory,Session>的形式存放到ThreadLocal的Map中,也就是說這兩者的上下文都是一個ThreadLocal的Map...查詢時以SessionFactory為鍵來查詢對應的Session,所以在同一執行緒中,一個SessionFactory只能有一個currentSession