Hibernate——ThreadLocal模式下管理的Session会在事务提交后自动关闭

本文通过一个小测试案例,深入探讨了Hibernate中的ThreadLocalSession模式。文章详细解析了一个因Session在事务提交后自动关闭而导致的异常问题,并追溯到Hibernate源码层面,揭示了在ThreadLocalSession模式下Session自动关闭的机制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

  最近对Hibernate的ThreadLocal Session模式有点兴趣。于是根据曹晓钢翻译的Hibernate Reference做了个小测验,结果发现了一个小bug。
代码很简单,都是利用Hibernate Reference中现成的代码。
首先是一个辅助的得到线程安全的session的HibernateUtil类,

public class HibernateUtil { 
    public static final SessionFactory sessionFactory; 
    static{ 
        try { 
            sessionFactory = new Configuration().configure().buildSessionFactory(); 
        } 
        catch(Throwable ex){ 
            throw new ExceptionInInitializerError(ex); 
        } 
    }

    public static final ThreadLocal session = new ThreadLocal(); 
    public static Session currentSession() { 
        Session s = (Session) session.get(); 
        if (s==null ) { 
            s = sessionFactory.getCurrentSession(); 
            session.set(s); 
        } 
        return s; 
    } 
    public static void closeSession() { 
        Session s = (Session) session.get(); 
        if (s!=null) 
        s.close(); 
        session.set(null); 
    } 
    public static SessionFactory getSessionFactory() { 
        return sessionFactory; 
    } 
} 

然后是一个测试插入数据的代码。也很简单,也是仿hibernate Reference上面的代码。

public class InsertUser { 
    public static void main(String[] args) { 
        Session session = HibernateUtil.currentSession(); 
        Transaction tx= session.beginTransaction(); 
        TUser user = new TUser(); 
        user.setName("Emma"); 
        session.save(user); 
        tx.commit(); 
        HibernateUtil.closeSession(); 
    } 
}

就这么简单一个程序,运行到最后,出现一个错误。

org.hibernate.SessionException: Session was already closed 
at org.hibernate.impl.SessionImpl.close(SessionImpl.Java:270) 
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) 
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) 
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) 
at java.lang.reflect.Method.invoke(Unknown Source) 
at org.hibernate.context.ThreadLocalSessionContext$TransactionProtectionWrapper.invoke(ThreadLocalSessionContext.java:301)
at $Proxy0.close(Unknown Source) 
at Util.HibernateUtil.closeSession(HibernateUtil.java:36) 
at test.InsertUser.main(InsertUser.java:20) 
Exception in thread "main"

错误出现在 HibernateUtil.closeSession(); 这一行,意思是session已经关闭了,再次关闭它就引起异常了。

不过前面的代码中只有个tx.commit(); 提交事务 而已,并没有自动关闭session啊?

于是把DEBUG信息调用出来,发现了以下几句提示:

DEBUG [main] - after transaction completion 
DEBUG [main] - automatically closing session 
DEBUG [main] - closing session 
DEBUG [main] - connection already null in cleanup : no action 
DEBUG [main] - allowing proxied method [close] to proceed to real session 
DEBUG [main] - closing session 
org.hibernate.SessionException: Session was already closed

特别是下面这3句话引起了我的注意,果然是session关闭了,而且是在 事务结束以后自动关闭的。

DEBUG [main] - after transaction completion 
DEBUG [main] - automatically closing session 
DEBUG [main] - closing session

那么这个机制是怎么发生的呢?

打开了Hibernate3的源码,我找到了答案。
首先,根据sessionFactory = new Configuration().configure().buildSessionFactory();
打开Configuration类的buildSessionFactory()方法,找到sessionFactory的生成语句

return new SessionFactoryImpl( 
    this, 
    mapping, 
    settings, 
    getInitializedEventListeners() 
); 

,然后找到SessionFactoryImpl的getCurrentSession方法,发现是这么定义的。

public org.hibernate.classic.Session getCurrentSession() throws HibernateException { 
    if ( currentSessionContext == null ) { 
        throw new HibernateException( "No CurrentSessionContext configured!" ); 
    } 
    return currentSessionContext.currentSession(); 
}

他调用的是一个currentSessionContext的currentSession方法。查找currentSessionContext变量,

currentSessionContext = buildCurrentSessionContext();

,知道了buildCurrentSessionContext方法产生了这个currentSessionContext 对象。

private CurrentSessionContext buildCurrentSessionContext() { 
    String impl = properties.getProperty( Environment.CURRENT_SESSION_CONTEXT_CLASS ); 
    // for backward-compatability 
    if ( impl == null && transactionManager != null ) { 
        impl = "jta"; 
    }
    if ( impl == null ) { 
        return null; 
    }else if ( "jta".equals( impl ) ) { 
        return new JTASessionContext( this ); 
    }else if ( "thread".equals( impl ) ) { 
        return new ThreadLocalSessionContext( this ); 
    }else { 
        try{ 
            Class implClass = ReflectHelper.classForName( impl ); 
            return ( CurrentSessionContext ) implClass 
            .getConstructor( new Class[] { SessionFactoryImplementor.class } ) 
            .newInstance( new Object[] { this } ); 
        }catch(Throwable t) { 
            log.error( "Unable to construct current session context [" + impl + "]", t ); 
            return null; 
        } 
    } 
}

这个方法就是用来判断使用JTA管理这个SessionContext还是用ThreadLocal来管理SessionContext的。
在我们这里是用 ThreadLocal 来管理的,于是找到了currentSessionContext 的实现类是 ThreadLocalSessionContext。

找到该类的currentSession方法

public final Session currentSession() throws HibernateException { 
    Session current = existingSession( factory ); 
    if (current == null) { 
        current = buildOrObtainSession(); 
        // register a cleanup synch 
        current.getTransaction().registerSynchronization( buildCleanupSynch() ); 
        // wrap the session in the transaction-protection proxy 
    if ( needsWrapping( current ) ) { 
        current = wrap( current ); 
    } 
    // then bind it 
    doBind( current, factory ); 
    } 
    return current; 
}

然后跟踪到 buildOrObtainSession(),就是这里,打开了session。

protected Session buildOrObtainSession() { 
    return factory.openSession( 
        null, isAutoFlushEnabled(), isAutoCloseEnabled(), getConnectionReleaseMode() 
    ); 
} 

注意第三个参数:isAutoCloseEnabled
打开Session这个接口,看到 openSession方法中这个参数是如下描述的:
* @param autoCloseSessionEnabled Should the session be auto-closed after
* transaction completion?

,就是说session是否应该在事务提交后自动关闭。

然后打开 ThreadLocalSessionContext 的isAutoCloseEnabled()方法。

/** 
* Mainly for subclass usage. This impl always returns true. 
* 
* @return Whether or not the the session should be closed by transaction completion. 
*/ 
protected boolean isAutoCloseEnabled() { 
    return true; 
} 

看到如下提示:Whether or not the the session should be closed by transaction completion ,即无论如何session应该在事务完成后关闭。

答案就在这里,就是说在ThreadLocal Session模式下面,只要提交了事务,那么session就自动关闭了,因此我参照Hibernate Refernece上面的代码写的在事务关闭以后再调用HibernateUtil.closeSession();是不对的,这句代码是完全多余的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值