`
laodaobazi
  • 浏览: 277008 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

Spring解决Hibernate session 关闭

阅读更多

在你得 web.xml 文件里面加上下面的配置信息:

 <filter>
   <filter-name>OpenSessionInViewFilter</filter-name> 
   <filter-class>org.springframework.orm.hibernate3.support.OpenSessionInViewFilter</filter-class> 
   </filter>
 <filter-mapping>
   <filter-name>OpenSessionInViewFilter</filter-name> 
   <url-pattern>/*</url-pattern> 
 </filter-mapping>

 

深入分析OpenSessionInViewFilter的内幕
最的在项目中发现Spring中对OpenSessionInViewFilter的作用,感觉很有意思,不由得自己的好奇心,开始研究起它的作用起来,总结如下:
Spring中对OpenSessionInViewFilter的描述如下:
     它是一个Servlet2.3过滤器,用来把一个Hibernate Session和一次完整的请求过程对应的线程相绑定。目的是为了实现"Open Session in View"的模式。例如: 它允许在事务提交之后延迟加载显示所需要的对象。
下面从处理请求的入口读起,下面所指的session均为hibernate session不再特别说明,本文观点纯属个人观点如有错误请批评指正不胜感激.

 

OpenSessionInViewFilter 的父类OncePerRequestFilter(抽象类)的方法,是过滤器的入口,是处理请求的第一个方法. 

public final void doFilter 
( ServletRequest request, ServletResponse response, FilterChain filterChain) 
        throws ServletException, IOException { 
    //首选判断进行过滤的是否是http请求 
    if (!(request instanceof HttpServletRequest) || !(response instanceof HttpServletResponse)) { 
        throw new ServletException("OncePerRequestFilter just supports HTTP requests"); 
    } 
    //如果是http请求的话进行强转 
    HttpServletRequest httpRequest = (HttpServletRequest) request; 
    HttpServletResponse httpResponse = (HttpServletResponse) response; 
    //alreadyFilteredAttributeName 是一个标识,用于判断是否需要进行OpenSessionInViewFilter 
    String alreadyFilteredAttributeName = getAlreadyFilteredAttributeName(); 
    if (request.getAttribute(alreadyFilteredAttributeName) != null || shouldNotFilter(httpRequest)) { 
        // Proceed without invoking this filter... 
        filterChain.doFilter(request, response); 
    } 
    else { 
        // Do invoke this filter... 
        request.setAttribute(alreadyFilteredAttributeName, Boolean.TRUE); 
        //下面这个方法是abstract方法由OpenSessionInViewFilter 实现,是OpenSessionInViewFilter 的核心方法 
        doFilterInternal(httpRequest, httpResponse, filterChain); 
    } 

好下面让我们进入doFilterInternal一探究竟




protected void doFilterInternal(HttpServletRequest request, 
        HttpServletResponse response, FilterChain filterChain) 
        throws ServletException, IOException { 
    /** 
     * 从spring的上下文中取得SessionFactory对象 
     * WebApplicationContext wac = WebApplicationContextUtils.getRequiredWebApplicationContext(getServletContext()); 
     * return (SessionFactory) wac.getBean(getSessionFactoryBeanName(),SessionFactory.class); 
     * getSessionFactoryBeanName()方法默认返回"sessionFactory"字符串,在spring配置文件中可要注意了,别写错了. 
     */ 
    SessionFactory sessionFactory = lookupSessionFactory(request); 
    boolean participate = false;// 标识过滤器结束时是否进行关闭session等后续处理 
    if (isSingleSession()) {//单session模式 
        //判断能否在当前线程中取得sessionFactory对象对应的session 
        if (TransactionSynchronizationManager.hasResource(sessionFactory)) { 
            //当能够找到session的时候证明会有相关类处理session的收尾工作,这个过滤器不能进行关闭session操作,否则会出现重复关闭的情况. 
            participate = true;//但我并没有想出正常使用的情况下什么时候会出现这种情况. 
        } else { 
            Session session = getSession(sessionFactory);//当前线程取不到session的时候通过sessionFactory创建,下面还会详细介绍此方法 
            //将session绑定到当前的线程中,SessionHolder是session的一层封装,里面有个存放session的map,而且是线程同步的Collections.synchronizedMap(new HashMap(1)); 
            //但是单session模式下一个SessionHolder对应一个session,核心方法是getValidatedSession 取得一个open状态下的session,并且取出后从map中移出. 
            TransactionSynchronizationManager.bindResource(sessionFactory, 
;                new SessionHolder(session)); 

        } 
    } else {//这段是非单session模式的处理情况,没有研究.但粗略看上去,大概思想是一样的 
        if (SessionFactoryUtils.isDeferredCloseActive(sessionFactory)) { 
            participate = true; 
        } else { 
            SessionFactoryUtils.initDeferredClose(sessionFactory); 
        } 
    } 
    try { 
        //将session绑定到了当前线程后,就该处理请求了 
        filterChain.doFilter(request, response); 
    }finally { 
        if (!participate) { 
            if (isSingleSession()) { 
                //当请求结束时,对于单session模式,这时候要取消session的绑定,因为web容器(Tomcat等)的线程是采用线程池机制的,线程使用过后并不会销毁. 
                SessionHolder sessionHolder = (SessionHolder) TransactionSynchronizationManager 
                        .unbindResource(sessionFactory); 
                //取消绑定只是取消session对象和线程对象之间的引用,还没有关闭session,不关闭session相对于不关闭数据库连接,所以这里要关闭session 
                closeSession(sessionHolder.getSession(), sessionFactory); 
            } else { 
                //非单session模式,没有研究. 
                SessionFactoryUtils.processDeferredClose(sessionFactory); 
            } 
        } 
    } 

下面详细介绍TransactionSynchronizationManager的几个关键的方法



public abstract class TransactionSynchronizationManager { 
//线程局部变量,为每一个使用该变量的线程都提供一个变量值的副本,每一个线程都可以独立地改变自己的副本,而不会与其它线程的副本冲突,用于存放session 
private static final ThreadLocal resources = new ThreadLocal(); 
public static boolean hasResource(Object key) {//判断当前线程是否已经绑定了session,key是sessionFactory对象,一个sessionFactory可以绑定一个session 
    Assert.notNull(key, "Key must not be null");//spring的Assert类不错,大家可以看看很简单 
    Map map = (Map) resources.get(); 
    return (map != null && map.containsKey(key)); 

public static void bindResource(Object key, Object value) throws IllegalStateException {//绑定session到当前线程 
    Assert.notNull(key, "Key must not be null"); 
    Assert.notNull(value, "Value must not be null"); 
    Map map = (Map) resources.get();//ThreadLocal对象只可以存放一个对象,所以使用map来扩展 
    if (map == null) { 
        map = new HashMap(); 
        resources.set(map); 
    } 
    if (map.containsKey(key)) { 
        throw new IllegalStateException("Already value [" + map.get(key) + "] for key [" + key + 
                "] bound to thread [" + Thread.currentThread().getName() + "]"); 
    } 
    map.put(key, value); 

static Object unbindResource(Object key) throws IllegalStateException {//取消当前线程对session的绑定 
    Assert.notNull(key, "Key must not be null"); 
    Map map = (Map) resources.get(); 
    if (map == null || !map.containsKey(key)) { 
        throw new IllegalStateException( 
                "No value for key [" + key + "] bound to thread [" + Thread.currentThread().getName() + "]"); 
    } 
    Object value = map.remove(key); 
    if (map.isEmpty()) { 
        resources.set(null); 
    } 
    return value; 

另外一个非常关键的方法是OpenSessionInViewFilter的getSession方法,我们看这个方法的关键并不是如何取得session,而且注意这里设置了FlushMode 
protected Session getSession(SessionFactory sessionFactory) 
        throws DataAccessResourceFailureException { 
    Session session = SessionFactoryUtils.getSession(sessionFactory, true); 
    FlushMode flushMode = getFlushMode();//默认情况下是FlushMode.NEVER 
    if (flushMode != null) { 
        session.setFlushMode(flushMode); 
    } 
    return session; 

读到这个地方,大家对ThreadLocal 感兴趣的话,可以看下我以前写的一篇文章http://blog.csdn.net/sunyujia/archive/2008/06/15/2549564.aspx
FlushMode.NEVER:
调用Session的查询方法时,不清理缓存
调用Session.commit()时,不清理缓存
调用Session.flush()时,清理缓存
不过FlushMode.NEVER已经不再建议使用了
官方描述如下
Deprecated. use MANUAL instead. 使用FlushMode.MANUAL来代替
The Session is never flushed unless Session.flush() is explicitly called by the application. This mode is very efficient for read only transactions. 
直到调用Session.flush()才会将变化反应到数据库,在只读的情况下是效率非常高的 
详见http://www.hibernate.org/hib_docs/v3/api/org/hibernate/FlushMode.html
我们来细看SessionFactoryUtils.getSession(sessionFactory, true);



public static Session getSession(SessionFactory sessionFactory, boolean allowCreate) throws DataAccessResourceFailureException, IllegalStateException { 
    try { 
        return doGetSession(sessionFactory, null, null, allowCreate); 
    } 
    catch (HibernateException ex) { 
        throw new DataAccessResourceFailureException("Could not open Hibernate Session", ex); 
    } 

从上面可以看出doGetSession才是真正的核心方法,这里非常重要的是HibernateTemplate也是调用此方法



protected Session getSession() { 
    if (isAlwaysUseNewSession()) { 
        return SessionFactoryUtils.getNewSession(getSessionFactory(), getEntityInterceptor()); 
    }else if (!isAllowCreate()) { 
        return SessionFactoryUtils.getSession(getSessionFactory(), false); 
    }else { 
        return SessionFactoryUtils.getSession( 
                getSessionFactory(), getEntityInterceptor(), getJdbcExceptionTranslator()); 
    } 

无论如何调用最后都是落实到SessionFactoryUtils.doGetSession这个方法上面,无论这个方法最后是否返回了null实际上在方法中都取得了session,
这是整个流程中最复杂的一个方法,部分代码我理解的也很不好,大家一起研究研究.



private static Session doGetSession( 
        SessionFactory sessionFactory, Interceptor entityInterceptor, 
        SQLExceptionTranslator jdbcExceptionTranslator, boolean allowCreate) 
        throws HibernateException, IllegalStateException { 
    Assert.notNull(sessionFactory, "No SessionFactory specified"); 
    //取当前线程绑定 
的session 
    SessionHolder sessionHolder = (SessionHolder) TransactionSynchronizationManager.getResource(sessionFactory); 
    //sessionHolder就可以看成是当前线程绑定的session了 
    if (sessionHolder != null && !sessionHolder.isEmpty()) { 
        Session session = null; 
        if (TransactionSynchronizationManager.isSynchronizationActive() & 
                sessionHolder.doesNotHoldNonDefaultSession()) {//判断spring的事务管理是否是激活的,同时SessionHolder对象中有且仅有一个session 
            // Spring transaction management is active -> 
            // register pre-bound Session with it for transactional flushing. 
            session = sessionHolder.getValidatedSession(); 
            //在开事务的时候HibernateTransactionManager类中的doBegin方法会将isSynchronizedWithTransaction设置为true,暂时不知道什么情况下会进入如下代码块 
            if (session != null && !sessionHolder.isSynchronizedWithTransaction()) { 
                TransactionSynchronizationManager.registerSynchronization( 
                        new SpringSessionSynchronization(sessionHolder, sessionFactory, jdbcExceptionTranslator, false)); 
                sessionHolder.setSynchronizedWithTransaction(true); 
                // Switch to FlushMode.AUTO, as we have to assume a thread-bound Session 
                // with FlushMode.NEVER, which needs to allow flushing within the transaction. 
                FlushMode flushMode = session.getFlushMode(); 
                if (flushMode.lessThan(FlushMode.COMMIT) & 
                        !TransactionSynchronizationManager.isCurrentTransactionReadOnly()) { 
                    session.setFlushMode(FlushMode.AUTO); 
                    sessionHolder.setPreviousFlushMode(flushMode); 
                } 
            } 
        } 
        else { 
            // No Spring transaction management active -> try JTA transaction synchronization. 
            //在没用事务的情况下下面的方法中只调用了sessionHolder.getValidatedSession(); 
            session = getJtaSynchronizedSession(sessionHolder, sessionFactory, jdbcExceptionTranslator); 
        } 
        if (session != null) { 
            return session;//在使用了OpenSessionInViewFilter的情况下,HibernateTemplate执行此方法会在这里return 
        } 
    } 
    //如果当前线程没有绑定session那么无论如何都是要创建session的,但是否会return还要取决于allowCreate等条件,在后面会看到 
    Session session = (entityInterceptor != null ? 
            sessionFactory.openSession(entityInterceptor) : sessionFactory.openSession()); 

    // Use same Session for further Hibernate actions within the transaction. 
    // Thread object will get removed by synchronization at transaction completion. 
    //判断spring的事务管理是否是激活的,目前我分析结果是在单session情况下,只有OpenSessionInViewFilter调用此方法,才会执行到这里,这种情况下是不会有事务的. 
    //可见下面if块里面的代码是针对非单session,并且有事务的情况处理的. 
    if (TransactionSynchronizationManager.isSynchronizationActive())& ;{ 
        // We're within a Spring-managed transaction, possibly from JtaTransactionManager. 
        logger.debug("Registering Spring transaction synchronization for new Hibernate Session"); 
        SessionHolder holderToUse = sessionHolder; 
        if (holderToUse == null) { 
            holderToUse = new SessionHolder(session); 
        } 
        else { 
            holderToUse.addSession(session); 
        } 
        if (TransactionSynchronizationManager.isCurrentTransactionReadOnly()) { 
            session.setFlushMode(FlushMode.NEVER);//只读情况下,可以提高效率同时也防止了脏数据等 
        } 
        TransactionSynchronizationManager.registerSynchronization( 
                new SpringSessionSynchronization(holderToUse, sessionFactory, jdbcExceptionTranslator, true)); 
        holderToUse.setSynchronizedWithTransaction(true);//这个变量真是很费解不知道什么情况下会为false呢? 
        if (holderToUse != sessionHolder) {//从这里可以看出非单session情况下,有事务也是要绑定session的只是颗粒度不同而已,我猜非单session事务结束后session就被关闭了. 
            TransactionSynchronizationManager.bindResource(sessionFactory, holderToUse); 
        } 
    } 
    else { 
        // No Spring transaction management active -> try JTA transaction synchronization. 
        registerJtaSynchronization(session, sessionFactory, jdbcExceptionTranslator, sessionHolder); 
    } 

    // Check whether we are allowed to return the Session. 
    //校验能否创建session的动作居然是放在创建session之后处理的,有点不解. 
    if (!allowCreate && !isSessionTransactional(session, sessionFactory)) { 
        closeSession(session); 
        throw new IllegalStateException("No Hibernate Session bound to thread, " + 
            "and configuration does not allow creation of non-transactional one here"); 
    } 
    return session; 

分享到:
评论

相关推荐

    Spring与Hibernate集成

    **Spring与Hibernate集成详解** 在Java企业级应用开发中,Spring和Hibernate是两个非常重要的框架。Spring是一个全方位的轻量级应用框架,提供了强大的依赖注入、AOP(面向切面编程)以及各种服务管理功能。而...

    springmvc+spring+hibernate

    Spring MVC、Spring 和 Hibernate 是Java Web开发中的三大主流框架,它们各司其职,共同构建了一个强大而灵活的后端架构。Spring MVC 负责处理HTTP请求并将其路由到相应的控制器,Spring 提供了依赖注入(DI)和面向...

    Spring+hibernate整合源代码

    1. **依赖注入**:Spring 提供的 DI(Dependency Injection)机制可以帮助管理 Hibernate SessionFactory 和 Session 实例,避免手动创建和管理这些对象。在配置文件中定义 SessionFactory 的 Bean,然后通过注解或 ...

    spring mvc + spring + hibernate 全注解整合开发视频教程 12

    Hibernate的Session API提供了CRUD操作,并且支持复杂的查询语句,包括HQL(Hibernate Query Language)和 Criteria API。 在Spring和Hibernate的整合中,Spring可以作为Hibernate的容器,管理SessionFactory和...

    org.hibernate.HibernateException: No Hibernate Session bound to thread

    Hibernate Session 绑定线程解决方案 在 Java web 开发中, Hibernate 是一个非常流行的 ORM(Object-Relational Mapping)框架,用于将 Java 对象映射到关系数据库中。然而,在使用 Hibernate 进行数据库操作时,...

    Struts2,Spring,Hibernate jar包下载

    HibernateJar包包含了Hibernate的核心API,如Session、SessionFactory、Query等,以及JPA支持和HQL(Hibernate Query Language)等。通过配置文件,开发者可以定义实体类和数据库表之间的映射关系,实现数据的自动...

    spring与hibernate的整合

    这里我们关注后者,通过 Spring 注入 SessionFactory 到 DAO 类中,然后在业务方法中打开和关闭 Session,处理事务。例如: ```java public class DepartmentDapImpl implements IDepartmentDao { private ...

    非注解Springmvc+spring+hibernate 入门实例

    此外,还需要创建`SessionFactory`,它是Hibernate的主要工作单元,负责创建`Session`对象,`Session`则是与数据库交互的接口。 在MvcSpringTest中,你可能还会看到数据访问对象(DAO)和业务服务对象(Service)的...

    Struts2+Spring+hibernate中对action的单元测试环境搭建[总结].pdf

    TransactionSynchronizationManager.bindResource(sessionFactory, new SessionHolder(hibernateSession)); ``` 然后,在类中加入以下两个私有函数: ```java private SessionFactory lookupSessionFactory...

    spring-hibernate3.jar.zip

    总的来说,"spring-hibernate3.jar"是一个便捷的集成解决方案,它使得Spring和Hibernate的配合更为紧密,提高了开发效率。但理解其背后的工作原理和配置方法,对于开发者来说同样重要,这将有助于他们更好地运用这两...

    spring整合hibernate示例代码

    这个示例代码可能包含了一个简单的SSH项目,展示了如何配置Spring的IoC(Inversion of Control,控制反转)容器来管理Hibernate的Session,以及如何在业务逻辑层和数据访问层之间建立联系。 "示例代码"意味着我们将...

    Spring与Hibernate集成中的session

    本文将详细探讨Spring与Hibernate的集成,特别是如何在集成环境中使用和管理`Session`。 首先,Spring作为一个轻量级的框架,提供了强大的依赖注入(DI)和面向切面编程(AOP)功能,它可以帮助我们解耦应用程序...

    Spring+Hibernate整合

    SessionFactory是Hibernate的核心组件,用于创建Session对象,而DataSource则负责连接数据库。这些配置通常会放在一个名为`applicationContext.xml`的XML文件中。 接下来,要将Hibernate的配置集成到Spring中,这...

    spring3 hibernate3 整合

    Spring3和Hibernate3的整合是企业级Java应用开发中的常见实践,旨在提供一个高效、灵活且可维护的解决方案。下面将详细阐述这两个框架的基本概念以及整合过程中的关键知识点。 Spring是一个全面的后端开发框架,它...

    Hibernate Session释放模式

    这是最常见的Session管理方式,通常与Spring的@Transactional注解结合使用。每个数据库事务开始时创建Session,事务结束时关闭Session。这种方式简单且易于理解,能保证事务的ACID特性,但可能会导致大量的短连接,...

    Spring整合Hibernate.jar

    SessionFactory是Hibernate的核心接口,用于创建Session对象,它是与数据库交互的入口点。 ```xml &lt;bean id="sessionFactory" class="org.springframework.orm.hibernate5.LocalSessionFactoryBean"&gt; &lt;!-- ...

    spring+hibernate整合简单示例

    总的来说,Spring和Hibernate的整合是Java Web开发中的常见实践,它结合了Spring的灵活控制和Hibernate的强大持久化能力,为开发者提供了高效、易维护的解决方案。通过学习和实践这个整合示例,你可以更好地理解和...

    Spring整合Hibernate案例

    在本文中,我们将深入探讨如何将Spring框架与Hibernate ORM(对象关系映射)框架整合,以便在实际项目中实现高效、灵活的数据访问层。Spring以其强大的依赖注入和管理能力,结合Hibernate的数据库操作功能,可以构建...

    Spring与hibernate 整合 简单示例

    在Java开发领域,Spring框架和Hibernate是两个非常重要的开源库,它们分别负责应用程序的依赖管理和对象关系映射(ORM)。Spring作为一个全面的轻量级应用框架,提供了事务管理、AOP(面向切面编程)、IoC(控制反转...

    ZK+spring+hibernate的整合

    4. **配置ZK-Spring**:需要引入ZK-Spring的库,这是一个连接ZK和Spring的桥梁,它提供了Spring的ApplicationContext到ZK Session的绑定,使得ZK组件可以直接使用Spring的Bean。 5. **事务管理**:由于Spring对事务...

Global site tag (gtag.js) - Google Analytics