博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
HibernateSessionFactory创建的Session是否单例
阅读量:6175 次
发布时间:2019-06-21

本文共 4425 字,大约阅读时间需要 14 分钟。

hot3.png

使用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实例 .并且每个线程都能创建一个实例.的这样一种单例注册表的机制来实现 .

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

 

转载于:https://my.oschina.net/91jason/blog/312393

你可能感兴趣的文章
MySQL表结构的导入和导出MySQL表结构的导入和导出
查看>>
JavaSE 学习参考:Map容器遍历
查看>>
salt模块命令
查看>>
基于TBDS的flume异常问题排查过程
查看>>
2017/5 JavaScript基础7--- 数组
查看>>
网络时常断网的解决办法
查看>>
第八次作业及答案
查看>>
linux 日志定时清理脚本
查看>>
java老司机面试题
查看>>
Guice AOP
查看>>
懒汉式单例
查看>>
java递归组装树形结构
查看>>
手把手教你自己写一个模糊搜索的下拉框
查看>>
.Net文档图像处理工具包GdPicture.NET发布v14.0.30,改进PDF/OCR生成速度
查看>>
NetBSD 8.1 RC1 发布
查看>>
12个必备的JavaScript装逼技巧
查看>>
域名备案图文教程
查看>>
iOS ScrollView上的view添加悬停效果
查看>>
Spring与MQ整合简单例子
查看>>
Apache-shiro学习
查看>>