資料整理

           

          ThreadLocal模式下管理的Session會在事務提交后自動關閉!

          ??
          縮略顯示????

          ThreadLocal模式下管理的Session會在事務提交后自動關閉!

          關鍵字: ? Hibernate????

          最近對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();是不對的,這句代碼是完全多余的。

          posted on 2006-12-18 09:13 謝瑋 閱讀(1813) 評論(1)  編輯  收藏 所屬分類: 數據持久化

          評論

          # re: ThreadLocal模式下管理的Session會在事務提交后自動關閉! 2008-11-07 15:08 劉鑫

          very good. 但是如果把tx.commit拿掉之后,數據沒有插入,但是數據表的標志位卻自增了,什么原因那?  回復  更多評論   

          導航

          統計

          常用鏈接

          留言簿(1)

          隨筆檔案

          文章分類

          文章檔案

          搜索

          最新評論

          主站蜘蛛池模板: 澄城县| 仙桃市| 桐城市| 法库县| 正蓝旗| 茶陵县| 健康| 黑龙江省| 蒙自县| 承德市| 集安市| 南通市| 仙居县| 锦州市| 长汀县| 广昌县| 泰安市| 涟水县| 仙居县| 柏乡县| 乌恰县| 嘉峪关市| 大足县| 鸡西市| 桑日县| 龙门县| 连云港市| 孟村| 平塘县| 阿巴嘎旗| 理塘县| 永和县| 达州市| 乳源| 鸡东县| 星子县| 光泽县| 南召县| 花垣县| 三亚市| 红原县|