資料整理

           

          ThreadLocal模式下管理的Session會在事務(wù)提交后自動關(guān)閉!

          ??
          縮略顯示????

          ThreadLocal模式下管理的Session會在事務(wù)提交后自動關(guān)閉!

          關(guān)鍵字: ? Hibernate????

          最近對Hibernate的ThreadLocal Session模式有點(diǎn)興趣。于是根據(jù)曹曉鋼翻譯的Hibernate Reference做了個小測驗(yàn),結(jié)果發(fā)現(xiàn)了一個小bug。
          代碼很簡單,都是利用Hibernate Reference中現(xiàn)成的代碼。
          首先是一個輔助的得到線程安全的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;
          }
          }
          然后是一個測試插入數(shù)據(jù)的代碼。也很簡單,也是仿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();
          }
          }

          就這么簡單一個程序,運(yùn)行到最后,出現(xiàn)一個錯誤。

          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"

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

          不過前面的代碼中只有個tx.commit(); 提交事務(wù) 而已,并沒有自動關(guān)閉session???

          于是把DEBUG信息調(diào)用出來,發(fā)現(xiàn)了以下幾句提示:
          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關(guān)閉了,而且是在 事務(wù)結(jié)束以后自動關(guān)閉的。
          DEBUG [main] - after transaction completion
          DEBUG [main] - automatically closing session
          DEBUG [main] - closing session

          那么這個機(jī)制是怎么發(fā)生的呢?

          打開了Hibernate3的源碼,我找到了答案。
          首先,根據(jù)sessionFactory = new Configuration().configure().buildSessionFactory();
          打開Configuration類的buildSessionFactory()方法,找到sessionFactory的生成語句
          return new SessionFactoryImpl(
          this,
          mapping,
          settings,
          getInitializedEventListeners()
          );
          ,然后找到SessionFactoryImpl的getCurrentSession方法,發(fā)現(xiàn)是這么定義的。

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

          他調(diào)用的是一個currentSessionContext的currentSession方法。查找currentSessionContext變量,

          currentSessionContext = buildCurrentSessionContext();

          ,知道了buildCurrentSessionContext方法產(chǎn)生了這個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 的實(shí)現(xiàn)類是 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()
          );
          }
          注意第三個參數(shù):isAutoCloseEnabled
          打開Session這個接口,看到 openSession方法中這個參數(shù)是如下描述的:
          * @param autoCloseSessionEnabled Should the session be auto-closed after
          * transaction completion?

          ,就是說session是否應(yīng)該在事務(wù)提交后自動關(guān)閉。

          然后打開 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應(yīng)該在事務(wù)完成后關(guān)閉。

          答案就在這里,就是說在ThreadLocal Session模式下面,只要提交了事務(wù),那么session就自動關(guān)閉了,因此我參照Hibernate Refernece上面的代碼寫的在事務(wù)關(guān)閉以后再調(diào)用HibernateUtil.closeSession();是不對的,這句代碼是完全多余的。

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

          評論

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

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

          導(dǎo)航

          統(tǒng)計(jì)

          常用鏈接

          留言簿(1)

          隨筆檔案

          文章分類

          文章檔案

          搜索

          最新評論

          主站蜘蛛池模板: 乐平市| 贵南县| 平潭县| 衡水市| 大庆市| 青阳县| 汝阳县| 时尚| 越西县| 白沙| 安多县| 永安市| 阳山县| 曲松县| 乐东| 巴彦淖尔市| 白银市| 无极县| 汉沽区| 饶阳县| 乐东| 日喀则市| 霍城县| 工布江达县| 佛坪县| 青川县| 郑州市| 肇州县| 大理市| 沈丘县| 新昌县| 克拉玛依市| 浪卡子县| 绥化市| 康保县| 永嘉县| 广州市| 彩票| 页游| 游戏| 陇西县|