`

OpenSessionInView

阅读更多
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继承,这种方法是我目前所使用的。
分享到:
评论

相关推荐

    Spring hibernate opensessioninview

    ### Spring + Hibernate OpenSessionInView 模式的理解和应用 在Java Web开发中,Spring与Hibernate作为两个重要的框架,经常被一起使用来实现业务逻辑与数据持久化的处理。而在使用这两个框架时,为了更好地管理...

    Struts Spring Hibernate 整合 OpenSessionInView 例子

    为了练手培训,给大家准备的 Open Session In View 的简单例子,纯代码,大家可以参考,其中主要说了六部分内容: 1.通过接口编程 2.通过spring注入dao到 action 3.通过 open session in view filter 支持 延迟加载...

    OpenSessionInView项目整合jar包

    在Java Web开发中,OpenSessionInView(OSIV)模式是一种常见的解决数据持久化问题的设计模式,主要用于Spring框架与Hibernate等ORM工具的集成。这个模式的主要目的是解决在HTTP请求处理过程中,由于Session范围内的...

    第30讲--Spring提供的CharacterEncoding和OpenSessionInView功能

    同时,需要注意的是,OpenSessionInView模式虽然方便,但也会带来潜在的问题,如事务边界不清晰和会话泄漏。因此,在实际应用中,应结合具体需求谨慎使用,并考虑使用更现代的解决方案,如Spring Data JPA的...

    SSH项目整合示例【OpenSessionInView】所用到的jar包

    OpenSessionInView(OSIV)模式是SSH整合中常见的一种优化策略,它在用户的一次HTTP请求过程中保持Hibernate Session,避免了多次打开和关闭Session,减少了N+1查询问题,提高了性能。 **Spring** 是一个全面的企业...

    Spring提供的CharacterEncoding和OpenSessionInView功能

    Spring框架是Java开发中不可或缺的一部分,它为开发者提供了丰富的功能,包括依赖注入、面向切面编程、事务管理等。在处理Web应用时,Spring提供了一些关键特性,如`CharacterEncodingFilter`和`...

    Open_Session_In_View详解.doc

    ### Open_Session_In_View详解 #### 一、背景与概念 在使用Hibernate进行对象持久化时,经常遇到的一个问题是关于懒加载(lazy loading)的处理。懒加载是一种优化技术,允许在真正需要某个关联对象的数据时才加载...

    spring中lazy=“true”的正常读取关联表(用opensessioninview)

    在Spring框架中,`lazy="true"` 是一个重要的特性,用于延迟加载(Lazy Loading)。它主要应用于数据持久层,如Hibernate等ORM框架中,目的是为了提高应用的性能。当一个对象被标记为懒加载时,只有在真正需要访问该...

    MyContextLoaderPlugIn.jar

    和Spring中OpenSessionInView由于org.springframework.web.struts.ContextLoaderPlugIn中保存同一个对象的名不同导致openSessionInView失效 稍微修改后在struts-config.xml中使用MyContextLoaderPlugIn.jar包中...

    SSH整合示例项目 hibernate5.2.3+struts2.5.2+spring4.3.3.zip

    - **二级缓存**:提高数据访问效率,OpenSessionInView模式下配合Spring实现 session级缓存。 **OpenSessionInView模式** OpenSessionInView模式是一种解决数据持久层和Web层之间事务管理的策略。在用户请求到达时...

    HibernateSpring多数据库解决方案.doc

    本方案主要探讨如何在基于Hibernate和Spring框架的环境中实现多数据库的管理,特别是在`OpenSessionInView`模式下的配置。 首先,我们看到在`applicationContext.xml`配置文件中定义了两个数据源,一个用于读操作...

    SSH整合 struts+hibernate+spring

    **OpenSessionInView模式**是一种常用的Hibernate优化模式,其主要目的是解决Hibernate的一级缓存问题。通过这种方式,可以确保在一个HTTP请求的生命周期内,Hibernate的Session始终处于打开状态,从而避免了因...

    struts+spring+hibernate面试题

    5. 延迟加载(Lazy Loading)和`openSessionInView`模式: - 延迟加载是在需要时才加载关联对象,确保只在session范围内加载,以提高性能。 - `openSessionInView`模式是在Web层使用Filter打开和关闭Session,确保...

    SSH全注解环境搭建

    - 配置OpenSessionInView过滤器以实现懒加载: ```xml <filter-name>openSessionInView <filter-class>org.springframework.orm.hibernate5.support.OpenSessionInViewFilter <filter-name>...

    SSH面试题

    - **OpenSessionInView**: 通过Web层的Filter在一次请求周期内保持Session打开,确保所有延迟加载的属性能在Session关闭前完成加载。 #### 八、Spring事务管理 **问题:** Spring支持几种事务管理方式?事务的隔离...

    OA项目SSH整合框架

    3,在web.xml中配置 spring 的 OpenSessionInView 过滤器(解决抛LazyInitializationException的问题) 1,配置 <!-- 配置 spring 的 OpenSessionInView 过滤器 --> <filter-name>OpenSessionInView ...

    spring+spingmvc+hibernate整合总结

    OpenSessionInView过滤器在Web请求的整个生命周期内保持一个Hibernate Session,使得在视图渲染阶段也能访问到数据库对象,避免了懒加载异常。 五、配置文件 1. `jdbc.properties`:存放数据库连接信息,如URL、...

    Java框架面试题

    7. **延迟加载与OpenSessionInView**:延迟加载(Lazy Loading)意味着关联对象在需要时才加载,而OpenSessionInView过滤器确保在HTTP请求生命周期内保持Session打开,以支持延迟加载。 8. **Spring事务管理**:...

    TSH练习例子

    文档讨论了HibernateSession的使用,提到了Session-per-Transaction(每个事务一个会话)和OpenSessionInView(在视图中打开会话)两种模式。前者更强调事务的一致性,后者则提高了懒加载性能,但可能会导致脏读问题...

    Spring框架1

    6. **Hibernate的延迟加载和OpenSessionInView**: - **延迟加载**:在同一个`Session`范围内,只有当需要数据时才加载,以提高性能。 - **OpenSessionInView**:在Web层通过Filter保持`Session`在整个HTTP请求...

Global site tag (gtag.js) - Google Analytics