`

Hibernate ThreadLocal 详解

阅读更多
Hibernate有很多值得学习的地方,这里我们主要介绍Hibernate ThreadLocal,包括介绍Hibernate官方开发手册标准示例等方面。

Hibernate ThreadLocal

它会为每个线程维护一个私有的变量空间。实际上, 其实现原理是在JVM 中维护一个Map,这个Map的key 就是当前的线程对象,而value则是 线程通过Hibernate ThreadLocal.set方法保存的对象实例。当线程调用Hibernate ThreadLocal.get方法时, Hibernate ThreadLocal会根据当前线程对象的引用,取出Map中对应的对象返回。

这样,Hibernate ThreadLocal通过以各个线程对象的引用作为区分,从而将不同线程的变量隔离开来。

Hibernate官方开发手册标准示例:

public class HibernateUtil {   private static SessionFactory sessionFactory;  static {   try {   // Create the SessionFactory sessionFactory = new Configuration().configure().buildSessionFactory();  }   catch (HibernateException ex) {   throw new RuntimeException( "Configuration problem: " + ex.getMessage(), ex );  }   }   public static final ThreadLocal session = new ThreadLocal();  public static Session currentSession() throws HibernateException {   Session s = (Session) session.get();  // Open a new Session, if this Thread has none yet if (s == null) {   s = sessionFactory.openSession();  session.set(s);  }   return s;  }   public static void closeSession() throws HibernateException {   Session s = (Session) session.get();  session.set(null);  if (s != null) s.close();  }   } 通过filter实现session的重用:

public class PersistenceFilter implements Filter {   
protected static ThreadLocal hibernateHolder = new ThreadLocal();  
public void doFilter(ServletRequest request,
ServletResponse response, FilterChain chain) 
throws IOException,ServletException {   
hibernateHolder.set(getSession());  
try {   
……   
chain.doFilter(request, response);  
……   
}   
finally {   
Session sess = (Session)hibernateHolder.get();  
if (sess != null) { hibernateHolder.set(null);  
try { sess.close(); } catch (HibernateException ex) {   
throw new ServletException(ex);  
}   
}   
}   
}   
……  
}
最近对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();是不对的,这句代码是完全多余的。
分享到:
评论

相关推荐

    Spring.3.x企业应用开发实战(完整版).part2

     《Spring3.x企业应用开发实战》是在《精通Spring2.x——企业应用开发详解》的基础上,经过历时一年的重大调整改版而成的,本书延续了上一版本追求深度,注重原理,不停留在技术表面的写作风格,力求使读者在熟练...

    Spring3.x企业应用开发实战(完整版) part1

     《Spring3.x企业应用开发实战》是在《精通Spring2.x——企业应用开发详解》的基础上,经过历时一年的重大调整改版而成的,本书延续了上一版本追求深度,注重原理,不停留在技术表面的写作风格,力求使读者在熟练...

    Spring 2.0 开发参考手册

    3.3.3. bean属性及构造器参数详解 3.3.4. 使用depends-on 3.3.5. 延迟初始化bean 3.3.6. 自动装配(autowire)协作者 3.3.7. 依赖检查 3.3.8. 方法注入 3.4. bean的作用域 3.4.1. Singleton作用域 3.4.2. ...

    spring chm文档

    3.3.3. bean属性及构造器参数详解 3.3.4. 使用depends-on 3.3.5. 延迟初始化bean 3.3.6. 自动装配(autowire)协作者 3.3.7. 依赖检查 3.3.8. 方法注入 3.4. bean的作用域 3.4.1. Singleton作用域 3.4.2. ...

    Spring中文帮助文档

    3.3.2. 依赖配置详解 3.3.3. 使用depends-on 3.3.4. 延迟初始化bean 3.3.5. 自动装配(autowire)协作者 3.3.6. 依赖检查 3.3.7. 方法注入 3.4. Bean的作用域 3.4.1. Singleton作用域 3.4.2. Prototype作用...

    Spring API

    3.3.2. 依赖配置详解 3.3.3. 使用depends-on 3.3.4. 延迟初始化bean 3.3.5. 自动装配(autowire)协作者 3.3.6. 依赖检查 3.3.7. 方法注入 3.4. Bean的作用域 3.4.1. Singleton作用域 3.4.2. Prototype作用...

    Spring-Reference_zh_CN(Spring中文参考手册)

    3.3.3. bean属性及构造器参数详解 3.3.3.1. 直接量(基本类型、Strings类型等。) 3.3.3.2. 引用其它的bean(协作者) 3.3.3.3. 内部bean 3.3.3.4. 集合 3.3.3.5. Nulls 3.3.3.6. XML-based configuration metadata ...

    java面试题以及技巧

    │ Struts+Hibernate+Spring轻量级J2EE企业应用实战.pdf │ Struts中文手册.pdf │ Struts配置文件详解.txt │ 上海税友.txt │ 上海税友软件 面试题.doc │ 公司培训文档-混淆的基本概念.doc │ 基本算法.doc │ ...

    java面试题目与技巧1

    │ Struts+Hibernate+Spring轻量级J2EE企业应用实战.pdf │ Struts中文手册.pdf │ Struts配置文件详解.txt │ 上海税友.txt │ 上海税友软件 面试题.doc │ 公司培训文档-混淆的基本概念.doc │ 基本算法.doc │ ...

    java面试题及技巧4

    │ Struts+Hibernate+Spring轻量级J2EE企业应用实战.pdf │ Struts中文手册.pdf │ Struts配置文件详解.txt │ 上海税友.txt │ 上海税友软件 面试题.doc │ 公司培训文档-混淆的基本概念.doc │ 基本算法.doc │ ...

    java面试题及技巧3

    │ Struts+Hibernate+Spring轻量级J2EE企业应用实战.pdf │ Struts中文手册.pdf │ Struts配置文件详解.txt │ 上海税友.txt │ 上海税友软件 面试题.doc │ 公司培训文档-混淆的基本概念.doc │ 基本算法.doc │ ...

    java面试题以及技巧6

    │ Struts+Hibernate+Spring轻量级J2EE企业应用实战.pdf │ Struts中文手册.pdf │ Struts配置文件详解.txt │ 上海税友.txt │ 上海税友软件 面试题.doc │ 公司培训文档-混淆的基本概念.doc │ 基本算法.doc │ ...

Global site tag (gtag.js) - Google Analytics