分享

HibernateSessionFactory创建的Session是否单例

 桑枯海 2012-11-22

使用Eclipse生成Hibernate 代码时候工具为我们生成了一个 HibernateSessionFactory 这样的类 来为我们提供获得Session的方法. 但是用这个类的时候我们发现一个问题. 看下面代码

Java代码  收藏代码
  1. Session session1= HibernateSessionFactory.getSession();        
  2. Session session2 = HibernateSessionFactory.getSession();  
  3. System.out.println(session1+"__"+session2);  
  4. System.out.print(session1==session2);  

 

通过测试发现 session1==session2 结果为 true .  难道真的是单例的吗?

我们来看一下HibernateSessionFactory 中 getSession()的实现

 

Java代码  收藏代码
  1. public static Session getSession() throws HibernateException {  
  2.         Session session = (Session) threadLocal.get();  
  3.   
  4.         if (session == null || !session.isOpen()) {  
  5.             if (sessionFactory == null) {  
  6.                 rebuildSessionFactory();  
  7.             }  
  8.             session = (sessionFactory != null) ? sessionFactory.openSession()  
  9.                     : null;  
  10.             threadLocal.set(session);  
  11.         }  
  12.   
  13.         return session;  
  14.     }  
  15.   
  16.    

在这个方法中我们看这样两行代码

1,Session session = (Session) threadLocal.get();

2,threadLocal.set(session);

这两行代码中 HibernateSessionFactory  在获得 session的时候是先去threadLocal上取 如果取到了就直接返回,取不到就 生成一个 并 通过set注册到当前线程上.

再来看一下 ThreadLocal 中 get ,set 的实现;

 

Java代码  收藏代码
  1. public T get() {  
  2.         Thread t = Thread.currentThread();  
  3.         ThreadLocalMap map = getMap(t);  
  4.         if (map != null)  
  5.             return (T)map.get(this);  
  6.   
  7.         // Maps are constructed lazily.  if the map for this thread  
  8.         // doesn't exist, create it, with this ThreadLocal and its  
  9.         // initial value as its only entry.  
  10.         T value = initialValue();  
  11.         createMap(t, value);  
  12.         return value;  
  13.     }  
Java代码  收藏代码
  1.    
Java代码  收藏代码
  1. public void set(T value) {  
  2.         Thread t = Thread.currentThread();  
  3.         ThreadLocalMap map = getMap(t);  
  4.         if (map != null)  
  5.             map.set(this, value);  
  6.         else  
  7.             createMap(t, value);  
  8.     }  

 

从它的get,set方法的实现中我们可以看出ThreadLocal 是以一种单例注册表的机制(一个单例类中包含一组对象的聚集) 实现的 通过它的实现(createMap(t,value)) 我们可以发现. 它保证在一个线程上只能注册一个实例. 并且每个线程上可以注册一个实例.因为不同的线程下Thread t = Thread.currentThread();
                                                                                     ThreadLocalMap map = getMap(t);

代表不同的对象 如果 t 是一个新的线程 那么 或的的map就是为空的 . 所以 createMap(t,value) 就会把一个新的实例放到这个线程上.并注册到注册表(ThreadLocalMap)中.

也就是说如果你在多个线程下创建了多个session 那么这些session在没有关闭的情况下都被保存在注册表(ThreadLocalMap)中 . ThreadLocalMap 中保存的就是一组这样的session对象.而不是一个.

下面是一个测试用来证明这一点.: 

 

Java代码  收藏代码
  1. class MySessionTest implements java.lang.Runnable{  
  2.     Session session=null;  
  3.     public void run()...{  
  4.         session= HibernateSessionFactory.getSession();      
  5.         System.out.println("创建完毕"+Thread.currentThread()+"::"+session);  
  6.     }  
  7.       
  8.     public Session getSesssionByThread(){  
  9.         return session;  
  10.     }  
  11. }  

 

 在main方法中写.

 

Java代码  收藏代码
  1. MySessionTest t1 = new MySessionTest();  
  2.        MySessionTest t2 = new MySessionTest();  
  3.        Thread tt1 = new Thread(t1);  
  4.        Thread tt2 = new Thread(t2);  
  5.        tt1.start();  
  6.        tt2.start();  
  7.          
  8.        tt1.sleep(1000);  
  9.        tt2.sleep(1000);  
  10.        Thread.sleep(1000);  
  11.        //如果线程不睡眠 下面的语句会先执行 得不到正确的结果. 只有等t1 ,和 t2 将session创建完毕后才能输出.      
  12.        System.out.println(t1.getSesssionByThread()==t2.getSesssionByThread());  

 

通过测试发现 输出结果为false   也就证明了 在多线程环境下HibernateSessionFactory.getSession()创建的Session不是单例的 .

但是又遇到了一个问题. 假设我要在同一个线程上通过HibernateSessionFactory获得多个Session的实例怎么办呢?

我们不妨在HibernateSessionFactory 中自己扩展一个方法

 

Java代码  收藏代码
  1. public static Session getAnotherSession() throws HibernateException{  
  2.         if (sessionFactory == null) {  
  3.             rebuildSessionFactory();  
  4.         }  
  5.         return sessionFactory.openSession();  
  6.                 
  7.     }  

 

通过这个方法获得的session实例 都上重新创建的新的势力.

看下面的测试代码

 

Java代码  收藏代码
  1. Session session1= HibernateSessionFactory.getSession();      
  2.        Session session2 = HibernateSessionFactory.getAnotherSession();      
  3.        Session session3 = HibernateSessionFactory.getAnotherSession();      
  4.          
  5.          
  6.        System.out.println(session1==session2);  
  7.        System.out.println(session2==session3);  
  8. nbsp;   

输出的结果为false ,false ; 也就是在同一线程下获得了3个不同的session实例.

 因为HIbernateSessionFactory是单 例的所以创建的SessionFactory也是单例的 .保证 SessionFactory 不被重复加载 ,而且扩展的这个方法.处在HIbernateSessionFactory 也不会重新去加载SessionFactory .

Eclipse为什么要把这个HIbernateSessionFactory获得的这个session定义为一个线程上只有一个实例呢?

个人认为:

1,session 在缓存在操作数据的时候应该具有隔离性. 也就是尽可能的将你要操做的一组数据放到同一个 session  缓存中 ,这样不至于在清理缓存的时候出现数据更新紊乱的情况.

2,session 不是线程安全的 , 在设计时候应该尽可能的避免多个线程共享一个session . 但又不能把session定义为单例. 所以就以一个线程上最多只能创建一个session实例 .并且每个线程都能创建一个实例.的这样一种单例注册表的机制来实现 .

关于单例类和单例注册表 可以参看我的另一片文章

http://blog.csdn.net/caoyinghui1986/archive/2008/05/22/2468180.aspx

 

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/caoyinghui1986/archive/2008/05/24/2476162.aspx

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多