hibernate之openSession 和 getCurrentSession 概述

来源:互联网 发布:mac上使用的ios模拟器 编辑:程序博客网 时间:2024/04/30 15:01

1.getCurrentSession与openSession的概述


1.openSession创建session时autoCloseSessionEnabled参数为false,即在事物结束后不会自动关闭session,需要手动关闭,如果不关闭将导致session关联的数据库连接无法释放,最后资源耗尽而使程序当掉。               

2.getCurrentSession创建session时autoCloseSessionEnabled,flushBeforeCompletionEnabled都为true,即事务提交后自动关闭session,并且session会同sessionFactory组成一个map(以sessionFactory为主键,注意一个应用中可以有多个sessionFactory)绑定到当前线程(ThreadLocal方式,以threadLocal实例作为key)。

3.getCurrentSession()与openSession()关联

 SessionFactory 启动的时候, Hibernate 会根据配置创建相应的 CurrentSessionContext ,在 getCurrentSession() 被调用的时候,实际被执行的方法是 CurrentSessionContext.currentSession() 

(1)在单独使用hibernate而没有集成spring时,需要指定CurrentSessionContext 的实现类:thread或jta。

 * 如果采用jdbc独立引用程序配置如下:
    <propertyname="hibernate.current_session_context_class">thread</property>
  * 如果采用了JTA事务配置如下  
   <property name="hibernate.current_session_context_class">jta</property>

thread和jta对应的CurrentSessionContext实现类在 currentSession() 执行时,如果当前 Session 为空,currentSession 会调用SessionFactory  openSession 创建一个新的session并且绑定到当前线程

(2)在集成spring时,内部会默认使用spring提供的CurrentSessionContext 实现类:SpingSessionContext,此时再配置hibernate.current_session_context_class属性指定CurrentSessionContext 实现类就无效,甚至会报异常。SpringSessionContext在currentSession()执行时,如果当前SpingSessionContext中session为空,则直接抛出异常“No Session found for current thread ”。那么集成spring时要怎么才能避免session不为空呢?方法:通过配置事务管理,让事务管理器自动打开session和事务,并绑定到SpingSessionContext中。也就是说某个方法要用到session且是通过getCurrentSession()方法获取时,要让该方法受事务的管理,如用@Transactional注解,或用<tx 标签设置事务管理范围。


 

2.getCurrentSession()原理

利于ThreadLocal模式管理Session。

3.ThreadLocal详解

         关键:

         1. Thread类内部有一个属性threadLocals,该属性的类型是ThreadLocal.ThreadLocalMap。即Thread类内部有一个属性,该属性是ThreadLocal类的内部类ThreadLocalMap,该内部类是一个map

         2.ThreadLocal.get()方法内部操作:获取当前线程的threadLocals,以当前ThreadLocal实例作为keythreadLocals中获取value

       3.ThreadLocal.set(t)方法内部操作:获取当前线程的threadLocals,以当前ThreadLocal实例作为keyt放入threadLocals

         4.ThreadLocal变量的作用是:通过set(t)方法将t放入当前线程的threadLocals(是一个map)变量中,以当前ThreadLocal实例为key。通过get()方法从当前线程的threadLocals变量中以当前ThreadLocal实例为key取出数据t。这样就是实现了:每个线程都有自己的变量t,而不会冲突。即ThreadLocal变量其实是一个工具,该工具用以把数据存储在当前线程中以及从当前线程中取出数据,而存数和取数都是以ThreadLocal实例为标志的。

         5.ThreadLocal变量一般都是用staticfinal修饰,因为:类A中声明了ThreadLocal变量:

         原因(1.一个线程先先后多次通过AThreadLocal变量存取数据,如果ThreadLocal变量是static的,即该实例前后不变同时同一个实例,所以可以准确定位到线程中之前存储的数据。相反,若是ThreadLocal变量不是static的,则每次调用类AThreadLocal变量时,该ThreadLocal变量都是新建的实例,这样就导致前后ThreadLocal变量实例不一致了,就无法找到以前依据ThreadLocal变量实例存储的数据,即依据新的ThreadLocal实例从当前线程中取出来的数据是null

         原因(2.不同的线程通过AThreadLocal变量存取数据都是用同一个ThreadLocal变量实例,ThreadLocal变量是static的。

 

         6.不同类中定义的ThreadLocal变量(应该都是staticfinal修饰的),即每个类都有自己的ThreadLocal实例,依据自己的ThreadLocal实例往线程中存取数据。这样同一个线程中的threadLocals变量中,每一个key就是一个ThreadLocal实例,代表了各自的数据。

 

 

         首先,ThreadLocal 不是用来解决共享对象的多线程访问问题的。一般情况下,通过ThreadLocal.set() 到线程中的对象是该线程自己使用的对象,其他线程是不需要访问的,也访问不到的。各个线程中访问的是不同的对象。 
        
         另外,说ThreadLocal使得各线程能够保持各自独立的一个对象,并不是通过ThreadLocal.set()来实现的,而是通过每个线程中的new 对象的操作来创建的对象,每个线程创建一个,不是什么对象的拷贝或副本。通过ThreadLocal.set()将这个新创建的对象的引用保存到各线程的自己的一个map中,每个线程都有这样一个map,执行ThreadLocal.get()时,各线程从自己的map中取出放进去的对象,因此取出来的是各自自己线程中的对象,ThreadLocal实例是作为mapkey来使用的 

如果ThreadLocal.set()进去的东西本来就是多个线程共享的同一个对象,那么多个线程的ThreadLocal.get()取得的还是这个共享对象本身,还是有并发访问问题。 

下面来看一个hibernate中典型的ThreadLocal的应用: 

1.   private static final ThreadLocal threadSession = new ThreadLocal();  

2.     

3.   public static Session getSession() throws InfrastructureException {  

4.       Session s = (Session) threadSession.get();  

5.       try {  

6.           if (s == null) {  

7.               s = getSessionFactory().openSession();  

8.               threadSession.set(s);  

9.           }  

10.      } catch (HibernateException ex) {  

11.          throw new InfrastructureException(ex);  

12.      }  

13.      return s;  

14.  }  


可以看到在getSession()方法中,首先判断当前线程中有没有放进去session,如果还没有,那么通过sessionFactory().openSession()来创建一个session,再将session set到线程中,实际是放到当前线程的ThreadLocalMap这个map中,这时,对于这个session的唯一引用就是当前线程中的那个ThreadLocalMap(下面会讲到),而threadSession作为这个值的key,要取得这个session可以通过threadSession.get()来得到,里面执行的操作实际是先取得当前线程中的ThreadLocalMap,然后将threadSession作为key将对应的值取出。这个session相当于线程的私有变量,而不是public的。 
显然,其他线程中是取不到这个session的,他们也只能取到自己的ThreadLocalMap中的东西。要是session是多个线程共享使用的,那还不乱套了。 
试想如果不用ThreadLocal怎么来实现呢?可能就要在action中创建session,然后把session一个个传到servicedao中,这可够麻烦的。或者可以自己定义一个静态的map,将当前thread作为key,创建的session作为值,putmap中,应该也行,这也是一般人的想法,但事实上,ThreadLocal的实现刚好相反,它是在每个线程中有一个map,而将ThreadLocal实例作为key,这样每个map中的项数很少,而且当线程销毁时相应的东西也一起销毁了,不知道除了这些还有什么其他的好处。 

总之,ThreadLocal不是用来解决对象共享访问问题的,而主要是提供了保持对象的方法和避免参数传递的方便的对象访问方式。归纳了两点: 
1
。每个线程中都有一个自己的ThreadLocalMap类对象,可以将线程自己的对象保持到其中,各管各的,线程可以正确的访问到自己的对象。 
2
。将一个共用的ThreadLocal静态实例作为key,将不同对象的引用保存到不同线程的ThreadLocalMap中,然后在线程执行的各处通过这个静态ThreadLocal实例的get()方法取得自己线程保存的那个对象,避免了将这个对象作为参数传递的麻烦。 


当然如果要把本来线程共享的对象通过ThreadLocal.set()放到线程中也可以,可以实现避免参数传递的访问方式,但是要注意get()到的是那同一个共享对象,并发访问问题要靠其他手段来解决。但一般来说线程共享的对象通过设置为某类的静态变量就可以实现方便的访问了,似乎没必要放到线程中。 

ThreadLocal的应用场合,我觉得最适合的是按线程多实例(每个线程对应一个实例)的对象的访问,并且这个对象很多地方都要用到。 

下面来看看ThreadLocal的实现原理(jdk1.5源码) 

1.   public class ThreadLocal<T> {  

2.       /** 

3.        * ThreadLocals rely on per-thread hash maps attached to each thread 

4.        * (Thread.threadLocals and inheritableThreadLocals).  The ThreadLocal 

5.        * objects act as keys, searched via threadLocalHashCode.  This is a 

6.        * custom hash code (useful only within ThreadLocalMaps) that eliminates 

7.        * collisions in the common case where consecutively constructed 

8.        * ThreadLocals are used by the same threads, while remaining well-behaved 

9.        * in less common cases. 

10.       */  

11.      private final int threadLocalHashCode = nextHashCode();  

12.    

13.      /** 

14.       * The next hash code to be given out. Accessed only by like-named method. 

15.       */  

16.      private static int nextHashCode = 0;  

17.    

18.      /** 

19.       * The difference between successively generated hash codes - turns 

20.       * implicit sequential thread-local IDs into near-optimally spread 

21.       * multiplicative hash values for power-of-two-sized tables. 

22.       */  

23.      private static final int HASH_INCREMENT = 0x61c88647;  

24.    

25.      /** 

26.       * Compute the next hash code. The static synchronization used here 

27.       * should not be a performance bottleneck. When ThreadLocals are 

28.       * generated in different threads at a fast enough rate to regularly 

29.       * contend on this lock, memory contention is by far a more serious 

30.       * problem than lock contention. 

31.       */  

32.      private static synchronized int nextHashCode() {  

33.          int h = nextHashCode;  

34.          nextHashCode = h + HASH_INCREMENT;  

35.          return h;  

36.      }  

37.    

38.      /** 

39.       * Creates a thread local variable. 

40.       */  

41.      public ThreadLocal() {  

42.      }  

43.    

44.      /** 

45.       * Returns the value in the current thread's copy of this thread-local 

46.       * variable.  Creates and initializes the copy if this is the first time 

47.       * the thread has called this method. 

48.       * 

49.       * @return the current thread's value of this thread-local 

50.       */  

51.      public T get() {  

52.          Thread t = Thread.currentThread();  

53.          ThreadLocalMap map = getMap(t);  

54.          if (map != null)  

55.              return (T)map.get(this);  

56.    

57.          // Maps are constructed lazily.  if the map for this thread  

58.          // doesn't exist, create it, with this ThreadLocal and its  

59.          // initial value as its only entry.  

60.          T value = initialValue();  

61.          createMap(t, value);  

62.          return value;  

63.      }  

64.    

65.      /** 

66.       * Sets the current thread's copy of this thread-local variable 

67.       * to the specified value.  Many applications will have no need for 

68.       * this functionality, relying solely on the {@link #initialValue} 

69.       * method to set the values of thread-locals. 

70.       * 

71.       * @param value the value to be stored in the current threads' copy of 

72.       *        this thread-local. 

73.       */  

74.      public void set(T value) {  

75.          Thread t = Thread.currentThread();  

76.          ThreadLocalMap map = getMap(t);  

77.          if (map != null)  

78.              map.set(this, value);  

79.          else  

80.              createMap(t, value);  

81.      }  

82.    

83.      /** 

84.       * Get the map associated with a ThreadLocal. Overridden in 

85.       * InheritableThreadLocal. 

86.       * 

87.       * @param  t the current thread 

88.       * @return the map 

89.       */  

90.      ThreadLocalMap getMap(Thread t) {  

91.          return t.threadLocals;  

92.      }  

93.    

94.      /** 

95.       * Create the map associated with a ThreadLocal. Overridden in 

96.       * InheritableThreadLocal. 

97.       * 

98.       * @param t the current thread 

99.       * @param firstValue value for the initial entry of the map 

100.      * @param map the map to store. 

101.      */  

102.     void createMap(Thread t, T firstValue) {  

103.         t.threadLocals = new ThreadLocalMap(this, firstValue);  

104.     }  

105.   

106.     .......  

107.   

108.     /** 

109.      * ThreadLocalMap is a customized hash map suitable only for 

110.      * maintaining thread local values. No operations are exported 

111.      * outside of the ThreadLocal class. The class is package private to 

112.      * allow declaration of fields in class Thread.  To help deal with 

113.      * very large and long-lived usages, the hash table entries use 

114.      * WeakReferences for keys. However, since reference queues are not 

115.      * used, stale entries are guaranteed to be removed only when 

116.      * the table starts running out of space. 

117.      */  

118.     static class ThreadLocalMap {  

119.   

120.     ........  

121.   

122.     }  

123.   

124. }  



可以看到ThreadLocal类中的变量只有这3int型: 

1.   private final int threadLocalHashCode = nextHashCode();  

2.   private static int nextHashCode = 0;  

3.   private static final int HASH_INCREMENT = 0x61c88647;  


而作为ThreadLocal实例的变量只有threadLocalHashCode这一个,nextHashCodeHASH_INCREMENTThreadLocal类的静态变量,实际上HASH_INCREMENT是一个常量,表示了连续分配的两个ThreadLocal实例的threadLocalHashCode值的增量,而nextHashCode的表示了即将分配的下一个ThreadLocal实例的threadLocalHashCode的值。 

可以来看一下创建一个ThreadLocal实例即new ThreadLocal()时做了哪些操作,从上面看到构造函数ThreadLocal()里什么操作都没有,唯一的操作是这句: 

1.   private final int threadLocalHashCode = nextHashCode();  


那么nextHashCode()做了什么呢: 

1.   private static synchronized int nextHashCode() {  

2.       int h = nextHashCode;  

3.       nextHashCode = h + HASH_INCREMENT;  

4.       return h;  

5.   }  

就是将ThreadLocal类的下一个hashCode值即nextHashCode的值赋给实例的threadLocalHashCode,然后nextHashCode的值增加HASH_INCREMENT这个值。 

因此ThreadLocal实例的变量只有这个threadLocalHashCode,而且是final的,用来区分不同的ThreadLocal实例,ThreadLocal类主要是作为工具类来使用,那么ThreadLocal.set()进去的对象是放在哪儿的呢? 

看一下上面的set()方法,两句合并一下成为 

1.   ThreadLocalMap map = Thread.currentThread().threadLocals;  


这个ThreadLocalMap类是ThreadLocal中定义的内部类,但是它的实例却用在Thread类中: 

1.   public class Thread implements Runnable {  

2.       ......  

3.     

4.       /* ThreadLocal values pertaining to this thread. This map is maintained 

5.        * by the ThreadLocal class. */  

6.       ThreadLocal.ThreadLocalMap threadLocals = null;    

7.       ......  

8.   }  



再看这句: 

1.   if (map != null)  

2.       map.set(this, value);  


也就是将该ThreadLocal实例作为key,要保持的对象作为值,设置到当前线程的ThreadLocalMap中,get()方法同样大家看了代码也就明白了,ThreadLocalMap类的代码太多了,我就不帖了,自己去看源码吧。


  

 

0 0