利于ThreadLocal管理Hibernate Session
关键字: hibernate 在利用Hibernate开发DAO模块时,我们和Session打的交道最多,所以如何合理的管理Session,避免Session的频繁创建和销毁,对于提高系统的性能来说是非常重要的,以往是通过eclipse的插件来自动完成这些代码的,当然效果是不错的,但是总是觉得不爽(没有读懂那些冗长的代码),所以现在打算自己实现Session管理的代码。我们知道Session是由SessionFactory负责创建的,而SessionFactory的实现是线程安全的,多个并发的线程可以同时访问一个SessionFactory并从中获取Session实例,那么Session是否是线程安全的呢?很遗憾,答案是否定的。Session中包含了数据库操作相关的状态信息,那么说如果多个线程同时使用一个Session实例进行CRUD,就很有可能导致数据存取的混乱,你能够想像那些你根本不能预测执行顺序的线程对你的一条记录进行操作的情形吗?
在Session的众多管理方案中,我们今天来认识一种名为ThreadLocal模式的解决方案。
早在Java1.2推出之时,Java平台中就引入了一个新的支持:java.lang.ThreadLocal,给我们在编写多线程程序时提供了一种新的选择。ThreadLocal是什么呢?其实ThreadLocal并非是一个线程的本地实现版本,它并不是一个Thread,而是thread local variable(线程局部变量)。也许把它命名为ThreadLocalVar更加合适。线程局部变量(ThreadLocal)其实的功用非常简单,就是为每一个使用该变量的线程都提供一个变量值的副本,是每一个线程都可以独立地改变自己的副本,而不会和其它线程的副本冲突。从线程的角度看,就好像每一个线程都完全拥有一个该变量。
ThreadLocal是如何做到为每一个线程维护变量的副本的呢?其实实现的思路很简单,在ThreadLocal类中有一个Map,用于存储每一个线程的变量的副本。比如下面的示例实现(为了简单,没有考虑集合的泛型):
- public class ThreadLocal {
- private Map values = Collections.synchronizedMap(new HashMap());
- public Object get() {
- Thread currentThread = Thread.currentThread();
- Object result = values.get(currentThread);
- if(result == null&&!values.containsKey(currentThread)) {
- result = initialValue();
- values.put(currentThread, result);
- }
- return result;
- }
- public void set(Object newValue) {
- values.put(Thread.currentThread(), newValue);
- }
- public Object initialValue() {
- return null;
- }
- }
那麽具体如何利用ThreadLocal来管理Session呢?Hibernate官方文档手册的示例之中,提供了一个通过ThreadLocal维护Session的好榜样:
- 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() throws HibernateException {
- Session s = session.get();
- if(s == null) {
- s = sessionFactory.openSession();
- session.set(s);
- }
- return s;
- }
- public static void closeSession() throws HibernateException {
- Session s = session.get();
- if(s != null) {
- s.close();
- }
- session.set(null);
- }
- }
只要借助上面的工具类获取Session实例,我们就可以实现线程范围内的Session共享,从而避免了线程中频繁的创建和销毁Session实例。当然,不要忘记在用完后关闭Session。
写到这里,想再多说一些,也许大多数时候我们的DAO并不会涉及到多线程的情形,比如我们不会将DAO的代码写在Servlet之中,那样不是良好的设计,我自己通常会在service层的代码里访问DAO的方法。但是我还是建议采用以上的工具类来管理Session,毕竟我们不能仅仅考虑今天为自己做什么,还应该考虑明天为自己做什么!
评论
每个Thread一个Session,在设计中要慎用Lazy Load。因为一个Bean(Hibernate Entity)一旦是Lazy Load,同时该Bean的生命周期超过创建该Bean的线程,或者两个以上线程共享该Bean时,就会造成抛出HibernateException,告诉你Session已经被Close。
1. 很蠢的方法:随时对Session进行Close。之所以说蠢,是因为这样完全失去了用ThreadLocal共享Session的意义。
2. 我目前用的方法:给Servlet加上Filter,在Filter中,对ThreadLocal中的Session进行Close操作。通过这样做,保证在一个Servlet请求的生命周期中,Session能复用。下一个Servlet请求即使和上一个复用同一个线程,也会新建Session。
这种做法,其实把Session的复用周期从“整个线程复用”减少到了“整个ServletRequest期间复用”
其实可能还有一种方式:在同一线程,且同一Http Session范围内复用Hibernate Session。这个我没有尝试,因为我做的这个项目的特点不适合这么做。但我想也是可行的。感兴趣的可以自己做一做。
应用服务器往往内部使用线程池。这会造成一个线程并不随着Servlet请求完成而结束。那么下一个Servlet请求就会复用这个线程的Session。后果是张三登陆后看到了李四的数据。
怎么解决? ==(离开一下)
http://www.hibernate.org/207.html
发表评论
- 浏览: 24458 次
- 性别:

- 来自: 哈尔滨

- 详细资料
搜索本博客
最新评论
-
Proxy Pattern Story
二楼的说的很有道理,楼主可否作个解释
-- by lkjust08 -
利于ThreadLocal管理Hibe ...
可以设置current_session_context_class参数
-- by wf_chn -
终于完成了搬家!
博主,偶很喜欢你的文章提点建议,这个模板真的有点花,看得有些吃力
-- by wf_chn -
利于ThreadLocal管理Hibe ...
再进一步说说: 每个Thread一个Session,在设计中要慎用Lazy Lo ...
-- by javapupil -
利于ThreadLocal管理Hibe ...
要解决这个问题,有两种做法: 1. 很蠢的方法:随时对Session进行Clos ...
-- by javapupil






评论排行榜