1. 程式人生 > >Hibernate——ThreadLocal模式下管理的Session會在事務提交後自動關閉

Hibernate——ThreadLocal模式下管理的Session會在事務提交後自動關閉

  最近對Hibernate的ThreadLocal Session模式有點興趣。於是根據曹曉鋼翻譯的Hibernate Reference做了個小測驗,結果發現了一個小bug。
程式碼很簡單,都是利用Hibernate Reference中現成的程式碼。
首先是一個輔助的得到執行緒安全的session的HibernateUtil類,

public class HibernateUtil { 
    public static final SessionFactory sessionFactory; 
    static{ 
        try { 
            sessionFactory = new
Configuration().configure().buildSessionFactory(); } catch(Throwable ex){ throw new ExceptionInInitializerError(ex); } } public static final ThreadLocal session = new ThreadLocal(); public static Session currentSession() { Session s = (Session) session.get
(); if (s==null ) { s = sessionFactory.getCurrentSession(); session.set(s); } return s; } public static void closeSession() { Session s = (Session) session.get(); if (s!=null) s.close(); session.set(null
); } public static SessionFactory getSessionFactory() { return sessionFactory; } }

然後是一個測試插入資料的程式碼。也很簡單,也是仿hibernate Reference上面的程式碼。

public class InsertUser { 
    public static void main(String[] args) { 
        Session session = HibernateUtil.currentSession(); 
        Transaction tx= session.beginTransaction(); 
        TUser user = new TUser(); 
        user.setName("Emma"); 
        session.save(user); 
        tx.commit(); 
        HibernateUtil.closeSession(); 
    } 
}

就這麼簡單一個程式,執行到最後,出現一個錯誤。

org.hibernate.SessionException: Session was already closed 
at org.hibernate.impl.SessionImpl.close(SessionImpl.Java:270) 
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) 
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) 
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) 
at java.lang.reflect.Method.invoke(Unknown Source) 
at org.hibernate.context.ThreadLocalSessionContext$TransactionProtectionWrapper.invoke(ThreadLocalSessionContext.java:301)
at $Proxy0.close(Unknown Source) 
at Util.HibernateUtil.closeSession(HibernateUtil.java:36) 
at test.InsertUser.main(InsertUser.java:20) 
Exception in thread "main"

錯誤出現在 HibernateUtil.closeSession(); 這一行,意思是session已經關閉了,再次關閉它就引起異常了。

不過前面的程式碼中只有個tx.commit(); 提交事務 而已,並沒有自動關閉session啊?

於是把DEBUG資訊調用出來,發現了以下幾句提示:

DEBUG [main] - after transaction completion 
DEBUG [main] - automatically closing session 
DEBUG [main] - closing session 
DEBUG [main] - connection already null in cleanup : no action 
DEBUG [main] - allowing proxied method [close] to proceed to real session 
DEBUG [main] - closing session 
org.hibernate.SessionException: Session was already closed

特別是下面這3句話引起了我的注意,果然是session關閉了,而且是在 事務結束以後自動關閉的。

DEBUG [main] - after transaction completion 
DEBUG [main] - automatically closing session 
DEBUG [main] - closing session

那麼這個機制是怎麼發生的呢?

打開了Hibernate3的原始碼,我找到了答案。
首先,根據sessionFactory = new Configuration().configure().buildSessionFactory();
開啟Configuration類的buildSessionFactory()方法,找到sessionFactory的生成語句

return new SessionFactoryImpl( 
    this, 
    mapping, 
    settings, 
    getInitializedEventListeners() 
); 

,然後找到SessionFactoryImpl的getCurrentSession方法,發現是這麼定義的。

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

他呼叫的是一個currentSessionContext的currentSession方法。查詢currentSessionContext變數,

currentSessionContext = buildCurrentSessionContext();

,知道了buildCurrentSessionContext方法產生了這個currentSessionContext 物件。

private CurrentSessionContext buildCurrentSessionContext() { 
    String impl = properties.getProperty( Environment.CURRENT_SESSION_CONTEXT_CLASS ); 
    // for backward-compatability 
    if ( impl == null && transactionManager != null ) { 
        impl = "jta"; 
    }
    if ( impl == null ) { 
        return null; 
    }else if ( "jta".equals( impl ) ) { 
        return new JTASessionContext( this ); 
    }else if ( "thread".equals( impl ) ) { 
        return new ThreadLocalSessionContext( this ); 
    }else { 
        try{ 
            Class implClass = ReflectHelper.classForName( impl ); 
            return ( CurrentSessionContext ) implClass 
            .getConstructor( new Class[] { SessionFactoryImplementor.class } ) 
            .newInstance( new Object[] { this } ); 
        }catch(Throwable t) { 
            log.error( "Unable to construct current session context [" + impl + "]", t ); 
            return null; 
        } 
    } 
}

這個方法就是用來判斷使用JTA管理這個SessionContext還是用ThreadLocal來管理SessionContext的。
在我們這裡是用 ThreadLocal 來管理的,於是找到了currentSessionContext 的實現類是 ThreadLocalSessionContext。

找到該類的currentSession方法

public final Session currentSession() throws HibernateException { 
    Session current = existingSession( factory ); 
    if (current == null) { 
        current = buildOrObtainSession(); 
        // register a cleanup synch 
        current.getTransaction().registerSynchronization( buildCleanupSynch() ); 
        // wrap the session in the transaction-protection proxy 
    if ( needsWrapping( current ) ) { 
        current = wrap( current ); 
    } 
    // then bind it 
    doBind( current, factory ); 
    } 
    return current; 
}

然後跟蹤到 buildOrObtainSession(),就是這裡,打開了session。

protected Session buildOrObtainSession() { 
    return factory.openSession( 
        null, isAutoFlushEnabled(), isAutoCloseEnabled(), getConnectionReleaseMode() 
    ); 
} 

注意第三個引數:isAutoCloseEnabled
開啟Session這個介面,看到 openSession方法中這個引數是如下描述的:
* @param autoCloseSessionEnabled Should the session be auto-closed after
* transaction completion?

,就是說session是否應該在事務提交後自動關閉。

然後開啟 ThreadLocalSessionContext 的isAutoCloseEnabled()方法。

/** 
* Mainly for subclass usage. This impl always returns true. 
* 
* @return Whether or not the the session should be closed by transaction completion. 
*/ 
protected boolean isAutoCloseEnabled() { 
    return true; 
} 

看到如下提示:Whether or not the the session should be closed by transaction completion ,即無論如何session應該在事務完成後關閉。

答案就在這裡,就是說在ThreadLocal Session模式下面,只要提交了事務,那麼session就自動關閉了,因此我參照Hibernate Refernece上面的程式碼寫的在事務關閉以後再呼叫HibernateUtil.closeSession();是不對的,這句程式碼是完全多餘的。

相關推薦

Hibernate——ThreadLocal模式管理Session事務提交自動關閉

  最近對Hibernate的ThreadLocal Session模式有點興趣。於是根據曹曉鋼翻譯的Hibernate Reference做了個小測驗,結果發現了一個小bug。 程式碼很簡單,都是利用Hibernate Reference中現成的程式碼。

ThreadLocal模式管理Session事務提交自動關閉

最近對Hibernate的ThreadLocal Session模式有點興趣。於是根據曹曉鋼翻譯的Hibernate Reference做了個小測驗,結果發現了一個小bug。 程式碼很簡單,都是利用Hibernate Reference中現成的程式碼。 首先是一個

ThreadLocal模式管理Session事務提交自動關閉

public class HibernateUtil { public static final SessionFactory sessionFactory; static{ try { sessionFactory = new Configuration().configure().buildSessio

ThreadLocal模式管理Session事務提交自動關閉!(摘自http://zgdhj95.javaeye.com/blog/36353)

最近對Hibernate的ThreadLocal Session模式有點興趣。於是根據曹曉鋼翻譯的Hibernate Reference做了個小測驗,結果發現了一個小bug。 程式碼很簡單,都是利用Hibernate Reference中現成的程式碼。 首先是一個輔助的得到執行緒安全

SQL Server事務日誌管理的進階,第5級:在完全恢復模式管理日誌

SQL Server事務日誌管理的進階,第5級:在完全恢復模式下管理日誌   原文連結:http://www.sqlservercentral.com/articles/Stairway+Series/73785/   託尼·戴維斯(Tony Davis)著,2012年1月27日

翻譯《Stairway to SQL Server Replication: Level 5- Managing the Log in Full Recovery Mode》 SQL Server事務日誌管理的進階,第5級:在完全恢復模式管理日誌

SQL Server事務日誌管理的進階,第5級:在完全恢復模式下管理日誌 SQL Server事務日誌管理的進階,第5級:在完全恢復模式下管理日誌 作者:託尼·戴維斯(Tony Davis) 時間:2012年1月27日  原文連結:http://www.sqlser

第五次翻譯:SQL Server事務日誌管理的進階,第5級:在完全恢復模式管理日誌

 SQL Server中事務日誌管理的階梯,第5級:在完全恢復模式下管理日誌 作者:Tony Davis,2012/01/27 文章轉載自:http://www.sqlservercentral.com/articles/Stairway+Series/73785/   該系列

form表單點選button按鈕提交頁面重新整理問題解決

button,input type=button按鈕在IE和w3c,firefox瀏覽器區別 當在IE瀏覽器下面時,button標籤按鈕,input標籤type屬性為button的按鈕是一樣的功能,不會對錶單進行任何操作。 但是在W3C瀏覽器,如Firefox下就需要注意了,button標籤按鈕會提

bootstrap的dropdown-menu(拉選單)點選選項關閉的方法

下面的例子是bootstrap下拉選單的例子 1、html <div class="dropdown"> <button type="button" class="btn dropdown-toggle" id="dro

Laravel框架在ubuntu的定時任務【過期訂單自動關閉

轉載了原作者的文章並結合自己的理解有所修改。Ubuntu 下,命令列輸入  crontab -e  打開了一個檔案  然後在最後加入  這技術定時器,* * * * * php /path-to-your-project/artisan schedule:run >&g

windows環境svn本地提交自動更新提交到伺服器的專案根目錄

【需求分析】:如果總是用FTP每次都得開啟FTP軟體,而且有時還會超時,並且存在程式碼衝突的問題,所以使用SVN替代FTP是個很不錯的選擇【注意事項】:在使用此功能之前先確保的你的本地客戶端和伺服器端的版本不能相差太大,否則可能會提示客戶端版本太舊等問題【具體細節】:至

linux監視程序 崩潰掛掉自動重啟的shell指令碼

如何保證服務一直執行?如何保證即使服務掛掉了也能自動重啟?在寫服務程式時經常會碰到這樣的問題。在Linux系統中,強大的shell就可以很靈活的處理這樣的事務。 下面的shell通過一個while-do迴圈,用ps -ef|grep 檢查loader程序是否正在執行,

解決Hbase啟動,hmaster在幾秒鐘自動關閉(停掉)!!!

兜兜轉轉,嘗試了好多種方法,終於是好了,真的是沒有度娘解決不了的問題!哈哈哈哈 在日誌(身為小白白的我,一開始日誌在哪我都不知道!路徑:/usr/local/hadoop/app/hbase-0.98.8/logs/hbase-hadoop-master-Master.l

ubuntu設定Iptables在重啟自動生效

save roles to a file iptables-save /etc/iptables.roles edit /etc/network/interfaces v

JmsTemplate 和activemq中事務提交執行訊息

   正常情況下,mq發出訊息後會立即執行,但是在分散式事務執行過程中,會有這樣的一種情況,即方法執行後,事務成功提交才希望訊息執行,這樣就需要配置一下JmsTemplate,兩種方法 1)在spring 配置檔案中新增jmsTemplate的屬性sessionTransa

sql伺服器第5級事務日誌管理的階梯:完全恢復模式的日誌管理

sql伺服器第5級事務日誌管理的階梯:完全恢復模式下的日誌管理 原文連結http://www.sqlservercentral.com/articles/Stairway+Series/73785/ 作者 Tony Davis, 2012/01/27   系列 本文是階

利用ThreadLocal模式管理Session

  我們知道Session是由SessionFactory負責建立的,而SessionFactory的實現是執行緒安全的,多個併發的執行緒可以同時訪問一個SessionFactory並從中獲取Session例項,那麼Session是否是執行緒安全的呢?很遺憾,答案是否定的。Session中包含了資料庫操作相

ThreadLocal管理Session

ThreadLocal並不是名字直接釋義的執行緒本地實現版本,其實它是一個執行緒中的區域性變數。 它的功能非常簡單,就是為使用某一變數的執行緒提供一個該變數的副本,使其可以對該副本進行操作,而不會影響

架構設計 | 基於Seata中介軟體,微服務模式事務管理

原始碼地址:[GitHub·點這裡](https://github.com/cicadasmile/spring-cloud-base) || [GitEE·點這裡](https://gitee.com/cicadasmile/spring-cloud-base) # 一、Seata簡介 ## 1、Sea

(2.7)備份與還原--在完全恢復模式事務日誌的角色

ges 需要 很多 對數 for 事情 mage .com .html 簡介 生產環境下的數據是如果可以寫在資產負債表上的話,我想這個資產所占的數額一定不會小。而墨菲定律(事情如果有變壞的可能,無論這種可能性有多小,它總會發生)仿佛是給DBA量身定做的。在上篇文章介