转载:OpenSessionInViewFilter

    技术2022-05-11  72

    转自:Potain 的BLOG

    OpenSessionInViewFilter

    Created by potian. Last edited by admin 61 days ago. Viewed 181 times. [edit] [attach] Hibernate的Lazy初始化1:n关系时,你必须保证是在同一个Session内部使用这个关系集合,不然Hiernate将抛出例外。

    另外,你不愿意你的DAO测试代码每次都打开关系Session,因此,我们一般会采用OpenSessionInView模式。

    OpenSessionInViewFilter解决Web应用程序的问题

    如果程序是在正常的Web程序中运行,那么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); }} 可 以看到,这个Filter在request开始之前,把sessionFactory绑定到 TransactionSynchronizationManager,和这个SessionHolder相关。这个意味着所有request执行过程中 将使用这个session。而在请求结束后,将和这个sessionFactory对应的session解绑,并且关闭Session。

    为什么绑定以后,就可以防止每次不会新开一个Session呢?看看HibernateDaoSupport的情况:

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

    我们的DAO将使用这个template进行操作:

    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,执行完就力争关闭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()); }} 而 这个SessionFactoryUtils能否得到当前的session以及closeSessionIfNecessary是否真正关闭 session,端取决于这个session是否用sessionHolder和这个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的问题

    使 用同样的方法,这两个Interceptor可以用来解决问题。但是关键的不同之处在于,它们的力度只能定义在DAO或业务方法上,而不是在我们的 Test方法上,除非我们把它们应用到TestCase的方法上,但你不大可能为TestCase去定义一个接口,然后把Interceptor应用到这 个接口的某些方法上。直接使用HibernateTransactionManager也是一样的。因此,如果我们有这样的测试:

    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执行都会启动一个session和关闭一个session 如果我们定义了一个lazy的关系,那么最后的Category savedChild = (Category ) savedParent.getChildren().get(0);将会让hibernate报错。

    解决方案

    一种方法是对TestCase应用Interceptor或者TransactionManager,但这个恐怕会造成很多麻烦。除非是使用增强方式的AOP.我前期采用这种方法(Aspectwerkz),在Eclipse里面也跑得含好。

    另一种方法是在TestCase的setup和teardown里面实现和Filter完全一样的处理,其他的TestCase都从这个TestCase继承,这种方法是我目前所使用的。

    Jolestar补充: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>

     

    最新回复(0)