OpenSessionInViewFilter解決Web應(yīng)用程序的問題 轉(zhuǎn)自:Potain 的BLOG

          OpenSessionInView

          Created by potian. Last edited by admin 61 days ago. Viewed 181 times.
          [edit] [attach]
          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的情況:

          publicfinal void setSessionFactory(SessionFactory sessionFactory) {
              this.hibernateTemplate = new HibernateTemplate(sessionFactory);
            }
           protectedfinal HibernateTemplate getHibernateTemplate() {
            return hibernateTemplate;
           }

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

          publicabstract class BaseHibernateObjectDao
          	extends HibernateDaoSupport
          	implements BaseObjectDao {

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

          publicObject doInHibernate(Session session) throws HibernateException { return session.get(getPersistentClass(), newLong(id)); }

          }); return obj; }

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

          public void remove(BaseEntityObject entity) { try {

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

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

          publicObject doInHibernate(Session session) throws HibernateException { session.refresh(entity); returnnull; }

          }); }

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

          publicObject doInHibernate(Session session) throws HibernateException { session.replicate(entity, ReplicationMode.OVERWRITE); returnnull; }

          }); }

          而HibernateTemplate試圖每次在execute之前去獲得Session,執(zhí)行完就力爭(zhēng)關(guān)閉Session
          publicObject 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綁定。
          publicstatic 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
          		thrownew CleanupFailureDataAccessException(
          		"Cannot close Hibernate session", ex.getSQLException());
          	}
          	catch (HibernateException ex) {
          		thrownew 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繼承,這種方法是我目前所使用的。

          Jolestar補(bǔ)充:openSessionInView的配置方法:

          ?? <filter>
          ? ? ? ? <filter-name>opensession</filter-name>
          ? ? ? ? <filter-class>org.springframework.orm.hibernate3.support.OpenSessionInViewFilter</filter-class>
          ? ? ? ? <init-param>
          ? ? ? ? ? ? <param-name>singleSession</param-name>
          ? ? ? ? ? ? <param-value>false</param-value>
          ? ? ? ? </init-param>
          ? ? </filter>

          posted on 2006-09-26 11:01 康文 閱讀(553) 評(píng)論(0)  編輯  收藏 所屬分類: java

          <2006年9月>
          272829303112
          3456789
          10111213141516
          17181920212223
          24252627282930
          1234567

          導(dǎo)航

          統(tǒng)計(jì)

          常用鏈接

          留言簿(1)

          隨筆分類

          隨筆檔案

          文章檔案

          搜索

          最新評(píng)論

          閱讀排行榜

          評(píng)論排行榜

          主站蜘蛛池模板: 晋中市| 遵义县| 鹤峰县| 鸡东县| 澄城县| 绥棱县| 郑州市| 惠东县| 宁南县| 略阳县| 博湖县| 广元市| 通榆县| 神木县| 凌源市| 镇安县| 鸡西市| 贵阳市| 鄂尔多斯市| 全南县| 马边| 舒兰市| 四会市| 怀安县| 溆浦县| 淳化县| 阿图什市| 六安市| 登封市| 宜章县| 韩城市| 喜德县| 岳池县| 广安市| 宾川县| 荣成市| 兴城市| 六枝特区| 洛南县| 长沙市| 应用必备|