Hibernate Session的生命週期受到其自身屬性和方法的影響,簡單的說:html
SessionFactory的openSession() 方法會開啓一個session。java
Session的flushMode會決定session什麼時候進行flush。linux
Session的flush()方法會對session進行強制flush。程序員
Session的close()方法會關閉session。web
然而在Spring託管中,session並非程序員本身控制的,session的生命週期交由Spring管理。影響session生命週期的狀況有這幾種:spring
使用SessionFactory.getCurrentSession()方法。sql
使用HibernateTemplate來屏蔽對session的直接訪問。session
使用HibernateTransactionManager,並在session使用區域外圍包裝了本地事務,甚至是事務嵌套。分佈式
使用了JtaTransactionManager,並在session使用區域外圍包裝了Jta事務,甚至是事務嵌套。this
使用了OpenSessionInViewFilter。
以上狀況都對Session的open時間, flushMode, flush時間和close時間有影響。
查看SessionFactoryImpl能夠看到,此方法從CurrentSessionContext中獲取存在其中的session。在非 Spring環境下,CurrentSessionContext的實現是由 「hibernate.current_session_context_class」屬性來控制的。可是在Spring環境 下,CurrentSessionContext的實現則變成了 SpringSessionContext或者SpringJtaSessionContext 。
getCurrentSession() { //詳細代碼請看org.hibernate.internal.SessionFactoryImpl:getCurrentSession return currentSessionContext.currentSession(); } // SpringSessionContext // 詳細代碼請看org.springframework.orm.hibernate4.SpringSessionContext:currentSession currentSession() { session = map.getSessionByKey(this.sessionFactory); if(session == null && existTransaction) { session = this.sessionFactory.openSession(); if(currentTransaction.readOnly) session.setFlushMode(MANUAL); else session.setFlushMode(AUTO); map.setSessionByKey(this.sessionFactory, session); } return session; }
上面的僞代碼中,假設map是一個存儲SessionFactory <-> session的地方(後面會分析map的具體實現和用途),currentSession方法會返回:
已有的session。
新建的session (當前沒有session,而且存在transaction)。並把FlushMode設置爲AUTO或者MANUAL(For readonly)。
throws exception (當前沒有session,也沒有transaction)。
包裝在HibernateTemplate中的session會受到Transaction的影響。
doExecute(callback) {// 查看org.springframework.orm.hibernate4.HibernateTemplate:doExecute try{ session = sessionFactory.getCurrentSession(); }catch (e){} if(session==null) { session = sessionFactory.openSession(); session.setFlushMode(MANUAL); isNew=true; } callback.invoke(); if(isNew) session.close(); }
由上面僞代碼能夠看出,在已經存在session或者transaction的環境下,session不受HibernateTemplate 的任何影響。若是當前不存在session和transaction,getCurrentSession會拋出exception。 HibernateTemplate會開啓一個新的session,並把flushMode設爲MANUAL,最後在callback結束後會關閉新建的 session。
HibernateTransactionManager爲本地事務管理器,它不支持分佈式數據源,它將事務交給了 openSession(), session.commint()和session.rollBack()。任何一個聲明瞭@Transactional的方法,都會被 org.springframework.transaction.interceptor.TransactionInterceptor作AOP切 割。其父類的方法invokeWithinTransaction執行了一個transaction。
invokeWithinTransaction(invocation) {// org.springframework.transaction.interceptor.TransactionAspectSupport tranInfo = createTransactionIfNecessary(); transactionManager.doBegin(tranInfo); invocation.proceed(); transactionManager.doCommit(tranInfo); transactionManager.doCleanupAfterCompletion(tranInfo); }
上面transactionManager是類HibernateTransactionManager的實例,且 doBegin,doCommit與doCleanupAfterCompletion爲類中的三個方法。上面僞代碼中的四行代碼對session的生命 週期相當重要。
createTransactionIfNecessary方法會建立一個包裝好的transaction,它包含了全部 transaction所須要的信息。但它不是UserTransaction和JDBC transaction,因此它只是一個虛擬的transaction實例。transaction實例會查看是否已經存在session或者 connection,若是有,則將其存入本身的域中。
第一步中生成的transaction是否已經存在session, 若是已經存在則跳過。若是沒有,真正調用SessionFactory的openSession方法,flushMode設爲AUTO。
先調用session.flush(), 再調用session.commit().
釋放connections。若是session是在第2步中新建立的,則執行session.close()或者註冊一個deffer close(OpenViewInFilter中的isSingleSession=false),不然什麼也不作。
在JTA事務下,userTransaction與session在表層代碼上不存在任何聯繫。session的生命週期是如何跟 userTransaction綁定在一塊兒的呢?它們之間的橋樑在 org.springframework.transaction.support.TransactionSynchronizationManager。 TransactionSynchronizationManager中定義的都是ThreadLocal變量,保存了當前線程中正在運行的 Transaction信息,以及SpringSessionSynchronization的實例。 TransactionSynchronizationManager中都是靜態方法,提供對變量的訪問。
每個SpringSessionSynchronization的實例中均保存了一對sessionFactory和session,並提供了對session的操做方法。咱們不須要關心這些方法,這些方法都是由TransactionManager自動調用的。
從下面一段代碼出發,咱們看看TransactionManager都作了什麼。
@Transactional public void transfer( amount, form, to) { Session sessionA = this.sessionFactoryA.getCurrentSession(); Account a = sessionA.load(Account.class,from ); a.setAmount(a.getAmount()-amount); Session sessionB = this.sessionFactoryB.getCurrentSession(); Account b = sessionB.load(Account.class, to); b.setAmount(b.getAmount()+amount); }
方法會被TransactionIntercepter切割。這部分的原代碼同HibernateTransactionManager是相同的,但爲了便於理解,我將改變一下僞代碼:
// org.springframework.transaction.interceptor.TransactionAspectSupport invokeWithinTransaction(invocation) { tranInfo = createTransactionIfNecessary(); transactionManager.doBegin(tranInfo) // 包含在源碼createTransactionIfNecessary()中,這裏拎出來單說 invocation.proceed(); transactionManager.triggerBeforeCommit(); transactionManager.doCommit(tranInfo); // 包含在源碼commitTransactionAfterReturning()中 transactionManager.triggerBeforeComplete() transactionManager.doCleanupAfterCompletion(tranInfo); // 包含在源碼commitTransactionAfterReturning()中 }
在執行方法前,intercepter會使用JtaTransactionManager啓動一個UserTransaction,並設置TransactionSynchronizationManager中的狀態,代表線程處在Transaction中。
sessionFactoryA.getCurrentSession()會使用已經存在的session,若是不存在,則調用 openSession()建立一個新的session,flushMode設爲AUTO。建立一個 SpringSessionSynchronization,將sessionA和sessionFactoryA放入其中。最後調用TransactionSynchronizationManager.registerSynchronization(springSessionSynchronization), 把SpringSessionSynchronization放入 TransactionSynchronizationManager 的synchronization集合中。 此段的源代碼能夠查看 org.springframework.orm.hibernate3.SessionFactoryUtils:doGetSession()。
sessionB的建立過程與步驟2相同,只是它擁有本身的SpringSessionSynchronization。
在triggerBeforeCommit()方法中,會循環取出 TransactionSynchronizationManager下的SpringSessionSynchronization,並調用它的beforeCommit方法,裏面會對其包含的session進行flush。
doCommit方法執行userTransaction的commit。
triggerBeforeComplete方法會循環調用SpringSessionSynchronization的 beforeComplete方法,若是session是在當前事務建立的,則調用它的close或defer close。若是是事務以前早已存在的session,則不進行close,只釋放connection。
不管是在JTA仍是本地事務狀況下,有幾個sessionFactory實例,就應該聲明多少個OSIF。每個OSIF對應一個sessionFactory,這樣才能爲每個sessionFactory都提早建立一個session。
OSIF下有一個屬性isSingleSession,若是爲true,則整個request中,相同的sessionFactory下只建立 一個session,並一直保持到filter結束。若是isSingleSession值爲false,OSIF不會建立session,它只會聲明在 此線程中每一個transaction中建立的session,都使用defer close。也就是在filter結束的時候纔會close。
以代碼爲比對:
@Transactional public void action() { Session s = sessionFactory.getCurrentSession(); ... ... } // with HibernateTemplate @Transactional public void action() { this.hibernateTemplate.save(); }
使用OSIF(isSingleSession=true) | 使用OSIF(isSingleSession=false) | 不用OSIF | |
在HibernateTransactionManager中 | OSIF建立session,FlushMode可配置 。 事務邊界獲取已有session。 事務開始。 進入action()。 獲取已有session。 執行操做。 退出action()。 事務邊界。 自動flush session。 事務提交。 OSIF中關閉session。 |
OSIF開啓defer close。 事務邊界建立新session(FlushMode=AUTO)。 事務開始。 進入action()。 獲取已有session。 執行操做。 退出action()。 事務邊界 自動flush session。 事務提交。 OSIF中關閉全部session。 |
事務邊界建立新session(FlushMode=AUTO)。 事務開始。 進入action()。 獲取已有session。 執行操做。 退出action()。 事務邊界 自動flush session。 事務提交。 自動關閉session。 |
在HibernateTransactionManager與 HibernateTemplate 中 | 同上 |
同上 | 同上 |
在JtaTransactionManager中 | OSIF建立session,FlushMode可配置 。 事務開始。 進入action()。 獲取已有session。 執行操做。 退出action()。 事務邊界。 自動flush session。 事務提交。 OSIF中關閉session。 |
OSIF開啓defer close。 事務開始。 進入action()。 獲取已有session(FlushMode=AUTO)。 執行操做。 退出action()。 事務邊界 自動flush session。 事務提交。 OSIF中關閉全部session。 |
事務開始。 進入action()。 建立新session(FlushMode=AUTO)。 執行操做。 退出action()。 事務邊界 自動flush session。 事務提交。 自動close session。 |
在 JtaTransactionManager與HibernateTemplate中 | 同上 | 同上 | 同上 |
不在Transaction中,並使用HibernateTemplate | OSIF建立session,flushMode可配 。 HibernateTemplate複用session。 session flush依賴template的flushMode屬性。 OSIF關閉session。 |
HibernateTemplate建立session。 session flush依賴template的flushMode屬性。 OSIF關閉session |