资料整理

 

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

  
缩略显示    

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

关键字:   Hibernate    

最近对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();是不对的,这句代码是完全多余的。

posted on 2006-12-18 09:13 谢玮 阅读(1784) 评论(1)  编辑  收藏 所属分类: 数据持久化

评论

# re: ThreadLocal模式下管理的Session会在事务提交后自动关闭! 2008-11-07 15:08 刘鑫

very good. 但是如果把tx.commit拿掉之后,数据没有插入,但是数据表的标志位却自增了,什么原因那?  回复  更多评论   


只有注册用户登录后才能发表评论。


网站导航:
 

导航

统计

常用链接

留言簿(1)

随笔档案

文章分类

文章档案

搜索

最新评论