文档章节

HibernateSessionFactory创建的Session是否单例

明舞
 明舞
发布于 2014/09/11 13:16
字数 1092
阅读 38
收藏 0

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

          Session session1  =   HibernateSessionFactory.getSession();      
        Session session2   =   HibernateSessionFactory.getSession();
        System.out.println(session1  +  "  __  "  +  session2);
        System.out.print(session1  ==  session2);

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

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

   public  static   Session getSession()   throws   HibernateException     {
        Session session = (Session) threadLocal.get();

        if (session == null || !session.isOpen()) {
            if (sessionFactory == null) {
                rebuildSessionFactory();
            }
            session = (sessionFactory != null) ? sessionFactory.openSession()
                    : null;
            threadLocal.set(session);
        }

        return session;
    }

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

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

2,threadLocal.set(session);

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

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

 public   T get()     {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            return (T)map.get(this);

        // Maps are constructed lazily.  if the map for this thread
        // doesn't exist, create it, with this ThreadLocal and its
        // initial value as its only entry.
        T value = initialValue();
        createMap(t, value);
        return value;
    }
 
    public     void   set(T value)     {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }

从它的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对象.而不是一个.

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

 class   MySessionTest   implements   java.lang.Runnable    {
    Session session=null;
    public void run(){
        session= HibernateSessionFactory.getSession();    
        System.out.println("创建完毕"+Thread.currentThread()+"::"+session);
    }
    
    public Session getSesssionByThread(){
        return session;
    }
}

在main方法中写.

         

MySessionTest t1   =     new   MySessionTest();
        MySessionTest t2   =     new   MySessionTest();
        Thread tt1   =     new   Thread(t1);
        Thread tt2   =     new   Thread(t2);
        tt1.start();
        tt2.start();
        
        tt1.sleep(  1000  );
        tt2.sleep(  1000  );
        Thread.sleep(  1000  );
          //  如果线程不睡眠 下面的语句会先执行 得不到正确的结果. 只有等t1 ,和 t2 将session创建完毕后才能输出.     
          System.out.println(t1.getSesssionByThread()  ==  t2.getSesssionByThread());


    

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

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

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

 public     static   Session getAnotherSession()   throws   HibernateException    {
        if (sessionFactory == null) {
            rebuildSessionFactory();
        }
        return sessionFactory.openSession();
              
    }

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

看下面的测试代码

        

 Session session1  =   HibernateSessionFactory.getSession();    
        Session session2   =   HibernateSessionFactory.getAnotherSession();    
        Session session3   =   HibernateSessionFactory.getAnotherSession();    
        
        
        System.out.println(session1  ==  session2);
        System.out.println(session2  ==  session3);


输出的结果为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

 

© 著作权归作者所有

明舞
粉丝 227
博文 424
码字总数 516555
作品 0
程序员
私信 提问
Hibernate 还算完整的通用DAO类

package dao; import org.hibernate.Session; import org.hibernate.Transaction; import org.hibernate.Query; import po.HibernateSessionFactory; import po.StuInfo; import po.StuMarks......

zting科技
2017/01/12
0
0
Hibernate+EhCache初级应用

1.EhCache是什么 EhCache是Hibernate的二级缓存技术之一,可以把查询出来的数据存储在内存或者磁盘,节省下次同样查询语句再次查询数据库,大幅减轻数据库压力; 2.EhCache的使用注意点 当用...

walb呀
2017/12/07
0
0
求指教。。单表junit测试mapping异常。。

create org.hibernate.dialect.MySQLDialect jdbc:mysql://localhost:3306/test root root com.mysql.jdbc.Driver com.mysql.jdbc.Driver true @Entity @Table(name="user") public class Us......

joelu
2012/05/19
322
0
Hibernate+ehcache二级缓存技术

1、首先设置EhCache,建立配置文件ehcache.XML,默认的位置在class-path,可以放到你的src目录下: <?xml version="1.0" encoding="UTF-8"?> <ehcache> <diskStore path="Java.io.tmpdi......

walb呀
2017/12/07
0
0
HibernateSessionFactory的用法

1-使用系统自带的HibernateSessionFactory类,该类是优秀的factory类。可以利用该类里的方法来完成我们一些需求 2-查询一条记录,查询多条记录 新建HibernateSessionFactory类。Myeclipse软件...

猴亮屏
2014/10/07
0
0

没有更多内容

加载失败,请刷新页面

加载更多

axios 使用步骤很简单,首先在前端项目中,引入 axios:

  前端网络访问,主流方案就是 Ajax,Vue 也不例外,在 Vue2.0 之前,网络访问较多的采用 vue-resources,Vue2.0 之后,官方不再建议使用 vue-resources ,这个项目本身也停止维护,目前建...

SEOwhywhy
17分钟前
1
0
c++ 创建对象的三种方法

c++有三种方法创建对象 结合代码来看 1 #include <iostream> 2 using namespace std; 3 class Test { 4 5 private: 6 public: 7 add() 8 { 9 ......

天王盖地虎626
31分钟前
0
0
ant 中的fileset include等拷贝

拷贝一个目录到指定目录下 例:<copy todir="${basedir}/new"> <fileset dir="${basedir}/old"> <include name="appgen" /> <include name="appgen/" /> <include name=appgen/**" /> <incl......

shzwork
37分钟前
2
0
react-jianshu项目的创建

创建项目 1、github上创建仓库react-jianshu 2、将项目克隆到本地git clone git@github.com:startjcu/react-jianshu.git 3、在当前目录(项目目录的上级目录)下执行create-react-app react-...

星闪海洋
46分钟前
2
0
OSChina 周二乱弹 —— 小哥哥,你可以教我写代码吗

Osc乱弹歌单(2019)请戳(这里) 【今日歌曲】 @nnnm: 生活大爆炸,结束了,这部陪伴了漫长时间的情景喜剧,最终是以诺贝尔奖和大团圆收尾的。虽然,不算精彩,但也是温馨。而少年谢尔顿的...

小小编辑
今天
564
13

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部