問題背景:
一個新同事接手一個對外接口調試的工作,不知道為什么,需要新開一個線程將對方傳過來的數據持久化到本地數據庫。就當他開心的開線程保存數據時,bug如約而至。看了問題,大致是因為沒有session(本地系統ORM用的是hibernate)。
在網上找了還一會,還真有這方面的解釋。
1、關於SessionFactory和Session
SessionFactory的實現是線程安全的,多個並發的線程可以同時訪問一 個SessionFactory並從中獲取Session實例,而Session不是線程安全的。Session中包含了數 據庫操作相關的狀態信息,那么說如果多個線程同時使用一個Session實例進行CRUD,就很有可能導致數據存取的混亂。
2、session的創建:
Hibernate中的Session通過SessionFactory來創建,具體的創建方式有兩種:
SessionFactory.getCurrentSession()、SessionFactory.openSession() ;
這兩種創建Session的方式主要有如下差別:
a:getCurrentSession創建的session會綁定到當前線程,而openSession不會。
b:getCurrentSession創建的線程會再事務回滾或事務提交后自動關閉,而openSession必須手動關閉
c:getCurrentsession需要在hibernate.cfg.xml文件中添加配置: <propertyname="current_session_context_class">thread</property>
關於current_session_context_class的設置參見:
3、current_session_context_class類型
這個參數設置hibernate管理session的方式(session的范圍)。hibernate共有三種方式並且都對應三個實現類實現接口org.hibernate.context.spi.CurrentSessionContext:
a、thread(org.hibernate.context.ThreadLocalSessionContext):當前session通過當前執行的線程來跟蹤和界定。
這樣配置是本地jdbc事物配置,通過getCurrentSession創建session綁定到當前線程。
這種方式創建的session中並沒有獲得transaction,我們都要手動調用session.beginTransaction()來打開一個活動的事物。
b、jta(org.hibernate.context.internal.JTASessionContext):當前session根據JTA來跟蹤和界定
c、manager(org.hibernate.context.internal.ManagerdSessionContext)
4、Spring實現的current_session_context_class類型
tspring整合hibernate后,由spring的TransactionManager管理hibernate的事務,currentSession是綁定到SpringSessionContext的,而不是thread,此時current_session_context_class的設置應該是spring對CurrentContextSession接口的實現類:SpringSessionContext。
a、@Transactional聲明的方法執行時,Spring的TransactionManager會自動Open Session,自動開啟事務,並且 將此Session綁定到SpringSessionContext(實際上是TransactionSynchronizationManager的Threadlocal的Map中)
b、SessionFactory.getCurrentSession()方法執行時,調用SpringSessionContext.currentSession()從TransactionSynchronizationManager的上下文中查找當前session
c、找到后返回當前Session,找不到返回HibernateException("No Sessionfound for current thread") ;
5、總結
使用hibernate進行數據庫操作時,需要session,並且需要對sesssion進行事務管理。hibernate有三種類型,界定了session的作用域。當使用spring來集成hibernate時,spring會幫我們管理session的事務,因此無需我們手動操作事務(提交、回滾等)。
當然在使用session的時候,注意session的范圍,當出現no session異常的情況時,查看配置文件,弄清楚設置session界定的范圍,然后定位問題找到解決方案。
同事的那個問題最后解決的方法就是在線程操作數據庫持久化的方法那里調用了注解@Transactiional來進行事務管理,為當前線程加入session解決了報錯問題。
關於current_session_context_class詳情看鏈接:http://blog.csdn.net/irelandken/article/details/7193123
