为啥 Spring 在使用 Hibernate 3 时推迟关闭 Hibernate 会话

Posted

技术标签:

【中文标题】为啥 Spring 在使用 Hibernate 3 时推迟关闭 Hibernate 会话【英文标题】:Why does Spring defer to close a Hibernate session when use Hibernate 3为什么 Spring 在使用 Hibernate 3 时推迟关闭 Hibernate 会话 【发布时间】:2014-09-08 23:46:23 【问题描述】:

背景

我正在努力将我们产品中使用的 Hibernate 3 升级到 Hibernate 4。我们使用的 Spring 版本是 Spring 3.2,并且我们的代码大量使用 Spring 3.2 的 HibernateTemplate,而 org.springframework.orm 包不支持它。 hibernate4 了。作为第一步,我的任务是编写一个自定义版本的 HibernateTemplate,它使用 SessionFactory 来获取会话并删除 Hibernate 3 的所有依赖项。

问题

当我阅读Spring 3.2的源码时,我注意到org.springframework.orm.hibernate3中的许多API在org.springframework.orm.hibernate4中被删除了.其中一种情况是 org.springframework.orm.hibernate3 包的 SessionFactoryUtils 中的 closeSessionOrRegisterDeferredClose 方法。

/**
 * Close the given Session or register it for deferred close.
 * @param session the Hibernate Session to close
 * @param sessionFactory Hibernate SessionFactory that the Session was created with
 * (may be @code null)
 * @see #initDeferredClose
 * @see #processDeferredClose
 */
static void closeSessionOrRegisterDeferredClose(Session session, SessionFactory sessionFactory) 
    Map<SessionFactory, Set<Session>> holderMap = deferredCloseHolder.get();
    if (holderMap != null && sessionFactory != null && holderMap.containsKey(sessionFactory)) 
        logger.debug("Registering Hibernate Session for deferred close");
        // Switch Session to FlushMode.MANUAL for remaining lifetime.
        session.setFlushMode(FlushMode.MANUAL);
        Set<Session> sessions = holderMap.get(sessionFactory);
        sessions.add(session);
    
    else 
        closeSession(session);
    

该方法由HibernateTemplate类的doExecute方法调用(最后一行代码)

/**
 * Execute the action specified by the given action object within a Session.
 * @param action callback object that specifies the Hibernate action
 * @param enforceNewSession whether to enforce a new Session for this template
 * even if there is a pre-bound transactional Session
 * @param enforceNativeSession whether to enforce exposure of the native
 * Hibernate Session to callback code
 * @return a result object returned by the action, or @code null
 * @throws org.springframework.dao.DataAccessException in case of Hibernate errors
 */
protected <T> T doExecute(HibernateCallback<T> action, boolean enforceNewSession, boolean enforceNativeSession)
        throws DataAccessException 

    Assert.notNull(action, "Callback object must not be null");

    Session session = (enforceNewSession ?
            SessionFactoryUtils.getNewSession(getSessionFactory(), getEntityInterceptor()) : getSession());
    boolean existingTransaction = (!enforceNewSession &&
            (!isAllowCreate() || SessionFactoryUtils.isSessionTransactional(session, getSessionFactory())));
    if (existingTransaction) 
        logger.debug("Found thread-bound Session for HibernateTemplate");
    

    FlushMode previousFlushMode = null;
    try 
        previousFlushMode = applyFlushMode(session, existingTransaction);
        enableFilters(session);
        Session sessionToExpose =
                (enforceNativeSession || isExposeNativeSession() ? session : createSessionProxy(session));
        T result = action.doInHibernate(sessionToExpose);
        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 
        if (existingTransaction) 
            logger.debug("Not closing pre-bound Hibernate Session after HibernateTemplate");
            disableFilters(session);
            if (previousFlushMode != null) 
                session.setFlushMode(previousFlushMode);
            
        
        else 
            // Never use deferred close for an explicitly new Session.
            if (isAlwaysUseNewSession()) 
                SessionFactoryUtils.closeSession(session);
            
            else 
                SessionFactoryUtils.closeSessionOrRegisterDeferredClose(session, getSessionFactory());
            
        
    

org.springframework.orm.hibernate4 包仍然有 SessionFactoryUtils 类,但删除了 closeSessionOrRegisterDeferredClose 方法。

问题

我想知道:

1) 在哪些情况下 Hibernate 3 会推迟关闭会话?

2) 如果我在 org.springframework.orm.hibernate4 中使用 Hibernate 4 会话和类,是否还需要关闭延迟?

【问题讨论】:

【参考方案1】:

Hibernate 不会关闭会话。由呼叫者关闭会话。在这种情况下,调用者是 spring,这取决于您如何设置事务管理。

Spring 将在您的事务提交时关闭会话,或者在网络请求完成时(如果您配置正确)可选地关闭会话。

如果您没有任何事务管理设置,模板将在执行您传递给它的回调后立即关闭会话。

您在 HolderMap 中看到的是事务管理的一部分。

PS:HibernateTemplate 在 hibernate4 包中消失了,但是在 spring 4 中做了一个补偿。在 spring 3 中做 hibernate 4 的首选方式是使用SessionFactory.getCurrentSession(),并让 spring 的事务支持管理打开和闭幕式。恕我直言,当他们这样做时,这是一个巨大的错误,但至少他们已经回溯了。

【讨论】:

也许你可以让答案不那么固执己见?例如,我认为您不应该再使用HibernateTemplate,因为它真的不存在了(从Hiberate 3.0.1 开始,引入CurrentSessionContext,模板或多或少是一种解决方法没有那个,它为 hibernate2 提供了有价值的异常翻译。现在所有这些都可以以更好的方式解决,从而导致更清洁的应用程序,即对 Spring API 的依赖更少。 我提出了 1 条意见。他们在 4 年春季将 HibernateTemplate 带回来的事实说明了人们想要它的事实。 它作为迁移选项被重新引入,也是之前 HibernateTemplate 的缩小版本,作为 sessionFactory.getCurrentSession 的简单包装器。 谢谢大家,我没有注意到 spring 4 带来了 HibernateTemplate。但是如果我坚持使用 Spring 3,是否有任何替代 spring hibernate 3 方法 closeSessionOrRegisterDeferredClose 的替代品?或者如果使用 Hibernate 4 会话,我不需要延迟关闭?

以上是关于为啥 Spring 在使用 Hibernate 3 时推迟关闭 Hibernate 会话的主要内容,如果未能解决你的问题,请参考以下文章

JPA Spring Boot Hibernate Rest API:为啥Hibernate在插入之前会删除?

为啥我们的 Spring/Hibernate/JDBC/Websphere 系统中的连接过早关闭?

Spring上的DBCP和Hibernate,不会重新打开死连接,为啥?

spring security:为啥我们不能在@PreAuthorize 中访问 Hibernate 实体参数?

为啥不推荐 HibernateDaoSupport?

Spring Data JPA 是不是在内部使用 Hibernate 以及如果我不提供方言属性,为啥我的应用程序正在运行?