我要啦免费统计

          微藍(lán)領(lǐng)域

          我的學(xué)習(xí)檔案館
          posts - 19, comments - 57, trackbacks - 0, articles - 57
            BlogJava :: 首頁 :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理

          關(guān)于OpenSessionInView

          Posted on 2008-03-28 20:38 hilor 閱讀(832) 評(píng)論(0)  編輯  收藏 所屬分類: SpringHibernate
          附http://www.javaeye.com/topic/14631
          OpenSessionInViewFilter討論
          Hibernate的Lazy初始化1:n關(guān)系時(shí),你必須保證是在同一個(gè)Session內(nèi)部使用這個(gè)關(guān)系集合,不然Hiernate將拋出例外。

          另外,你不愿意你的DAO測(cè)試代碼每次都打開關(guān)系Session,因此,我們一般會(huì)采用OpenSessionInView模式。

          OpenSessionInViewFilter解決Web應(yīng)用程序的問題

          如果程序是在正常的Web程序中運(yùn)行,那么Spring的OpenSessionInViewFilter能夠解決問題,它:
          protected void doFilterInternal(HttpServletRequest request, 
          HttpServletResponse response,
                 FilterChain filterChain) throws ServletException, IOException {
                SessionFactory sessionFactory = lookupSessionFactory();
                logger.debug("Opening Hibernate Session in OpenSessionInViewFilter");
                Session session = getSession(sessionFactory);
                TransactionSynchronizationManager.bindResource(sessionFactory,
          new SessionHolder(session));
                try {
                      filterChain.doFilter(request, response);
                }
                finally {
                      TransactionSynchronizationManager.unbindResource(sessionFactory);
                      logger.debug("Closing Hibernate Session in OpenSessionInViewFilter");
                      closeSession(session, sessionFactory);
                }
          }
          可以看到,這個(gè)Filter在request開始之前,把sessionFactory綁定到TransactionSynchronizationManager,和這個(gè)SessionHolder相關(guān)。這個(gè)意味著所有request執(zhí)行過程中將使用這個(gè)session。而在請(qǐng)求結(jié)束后,將和這個(gè)sessionFactory對(duì)應(yīng)的session解綁,并且關(guān)閉Session。

          為什么綁定以后,就可以防止每次不會(huì)新開一個(gè)Session呢?看看HibernateDaoSupport的情況:

          public final void setSessionFactory(SessionFactory sessionFactory) {
          this.hibernateTemplate = new HibernateTemplate(sessionFactory);
          }
          protected final HibernateTemplate getHibernateTemplate() {
          return hibernateTemplate;
          }

          我們的DAO將使用這個(gè)template進(jìn)行操作:

          public abstract class BaseHibernateObjectDao
                extends HibernateDaoSupport
                implements BaseObjectDao {


                protected BaseEntityObject getByClassId(final long id) {
                      BaseEntityObject obj =
                            (BaseEntityObject) getHibernateTemplate()
                                  .execute(new HibernateCallback() {


                            public Object doInHibernate(Session session)
                                  throws HibernateException {
                                  return session.get(getPersistentClass(),
          new Long(id));
                            }


                      });
                      return obj;
                }



                public void save(BaseEntityObject entity) {
                      getHibernateTemplate().saveOrUpdate(entity);
                }


                public void remove(BaseEntityObject entity) {
                      try {


                            getHibernateTemplate().delete(entity);
                      } catch (Exception e) {
                            throw new FlexEnterpriseDataAccessException(e);
                      }
                }


                public void refresh(final BaseEntityObject entity) {
                      getHibernateTemplate().execute(new HibernateCallback() {


                            public Object doInHibernate(Session session)
                                  throws HibernateException {
                                  session.refresh(entity);
                                  return null;
                            }


                      });
                }


                public void replicate(final Object entity) {
                      getHibernateTemplate().execute(new HibernateCallback() {


                            public Object doInHibernate(Session session)
                                  throws HibernateException {
                                  session.replicate(entity,
          ReplicationMode.OVERWRITE);
                                  return null;
                            }


                      });
                }

          而HibernateTemplate試圖每次在execute之前去獲得Session,執(zhí)行完就力爭(zhēng)關(guān)閉Session
          public Object execute(HibernateCallback action) throws DataAccessException {
                Session session = (!this.allowCreate ?
                      SessionFactoryUtils.getSession(getSessionFactory(),
          false) :
                      SessionFactoryUtils.getSession(getSessionFactory(),
          getEntityInterceptor(),
          getJdbcExceptionTranslator()));
                boolean existingTransaction =
          TransactionSynchronizationManager.hasResource(getSessionFactory());
                if (!existingTransaction && getFlushMode() == FLUSH_NEVER) {
                      session.setFlushMode(FlushMode.NEVER);
                }
                try {
                      Object result = action.doInHibernate(session);
                      flushIfNecessary(session, existingTransaction);
                      return result;
                }
                catch (HibernateException ex) {
                      throw convertHibernateAccessException(ex);
                }
                catch (SQLException ex) {
                      throw convertJdbcAccessException(ex);
                }
                catch (RuntimeException ex) {
                      // callback code threw application exception
                      throw ex;
                }
                finally {
                      SessionFactoryUtils.closeSessionIfNecessary(
          session, getSessionFactory());
                }
          }
          而這個(gè)SessionFactoryUtils能否得到當(dāng)前的session以及closeSessionIfNecessary是否真正關(guān)閉session,端取決于這個(gè)session是否用sessionHolder和這個(gè)sessionFactory在我們最開始提到的TransactionSynchronizationManager綁定。
          public static void closeSessionIfNecessary(Session session, 
          SessionFactory sessionFactory)
          throws CleanupFailureDataAccessException {
                if (session == null ||
                 TransactionSynchronizationManager.hasResource(sessionFactory)) {
                      return;
                }
                logger.debug("Closing Hibernate session");
                try {
                      session.close();
                }
                catch (JDBCException ex) {
                      // SQLException underneath
                      throw new CleanupFailureDataAccessException(
                      "Cannot close Hibernate session", ex.getSQLException());
                }
                catch (HibernateException ex) {
                      throw new CleanupFailureDataAccessException(
                      "Cannot close Hibernate session", ex);
                }
          }

          HibernateInterceptor和OpenSessionInViewInterceptor的問題

          使用同樣的方法,這兩個(gè)Interceptor可以用來解決問題。但是關(guān)鍵的不同之處在于,它們的力度只能定義在DAO或業(yè)務(wù)方法上,而不是在我們的Test方法上,除非我們把它們應(yīng)用到TestCase的方法上,但你不大可能為TestCase去定義一個(gè)接口,然后把Interceptor應(yīng)用到這個(gè)接口的某些方法上。直接使用HibernateTransactionManager也是一樣的。因此,如果我們有這樣的測(cè)試:

          Category parentCategory  = new Category ();
                parentCategory.setName("parent");
                dao.save(parentCategory);

                Category childCategory = new Category();
          childCategory.setName("child");


                parentCategory.addChild(childCategory);
                dao.save(childCategory);


                Category savedParent = dao.getCategory("parent");
                Category savedChild = (Category ) savedParent.getChildren().get(0);
                assertEquals(savedChild, childCategory);

          將意味著兩件事情:
          • 每次DAO執(zhí)行都會(huì)啟動(dòng)一個(gè)session和關(guān)閉一個(gè)session
          • 如果我們定義了一個(gè)lazy的關(guān)系,那么最后的Category savedChild = (Category ) savedParent.getChildren().get(0);將會(huì)讓hibernate報(bào)錯(cuò)。

          解決方案

          一種方法是對(duì)TestCase應(yīng)用Interceptor或者TransactionManager,但這個(gè)恐怕會(huì)造成很多麻煩。除非是使用增強(qiáng)方式的AOP.我前期采用這種方法(Aspectwerkz),在Eclipse里面也跑得含好。

          另一種方法是在TestCase的setup和teardown里面實(shí)現(xiàn)和Filter完全一樣的處理,其他的TestCase都從這個(gè)TestCase繼承,這種方法是我目前所使用的。

          主站蜘蛛池模板: 民丰县| 日喀则市| 香河县| 嘉禾县| 浦北县| 容城县| 万盛区| 红安县| 新源县| 望江县| 成安县| 枞阳县| 连江县| 武功县| 固阳县| 光泽县| 石楼县| 富裕县| 宣恩县| 新乡市| 郧西县| 宁德市| 寻乌县| 漠河县| 阿克陶县| 景洪市| 宜黄县| 龙游县| 富源县| 石阡县| 天长市| 青神县| 临沂市| 淮滨县| 泰兴市| 黄梅县| 宣威市| 永靖县| 竹溪县| 慈利县| 平昌县|