hibernate中的ThreadLocalSessionContext和Transaction的關係
最近在做一個鐵科院的醫保專案框架是SSH,這種經典框架相信很多人對這個都很熟悉,不過對有些概念性的東西不知道你是否清楚,例如我們都知道hibernate通過session來操作資料庫完成CRUD操作,Spring在同hibernate整合的時候可以將hibernate的sessionfactory拿過來管理,為了更方便操作資料庫spring封裝了一個getHibernateTemplate類,該類也可以完成CRUD操作,在使用靈活行方面比hibernate靈活了一些,操作也簡便。
對資料庫的操作都需要控制事務保證操作原子性,session在操作資料庫的時候事務是怎麼控制的呢,我們只知道事務是隨著session一起開啟和關閉的,具體來看一下是如何實現。
通過寫底層的一些方法,瞭解了getCurrentSession()與openSession()的區別,前者是從當前執行緒中獲取session,使用完後不需要自己關閉;後者是開啟一個新的session,需要自己手動開發和關閉;
我們操作的是session,它和事務有什麼關係呢?換句話說,session和事務生命週期是否相同?分別何時開啟?何時關閉?
想要清楚這兩者的關係,先要了解session機制,session是hibernate框架中一個操作ORM對映關係的介面,該介面有三種類型,分別為ThreadLocalSessionContext、JTASessionContext、ManagedSessionContext,這三個實現類實現了CurrentSessionContext介面。
getCurrentSession()這個方法得到的就是CurrentSessionContext介面,提供的預設實現類為ThreadLocalSessionContext
<span style="font-size:14px;">SessionFactory類 public abstract interface SessionFactory extends Referenceable, Serializable { public abstract Session openSession(Connection paramConnection); public abstract Session getCurrentSession() throws HibernateException; } SessionFactory實現類 public final class SessionFactoryImpl implements SessionFactory, SessionFactoryImplementor { private final transient CurrentSessionContext currentSessionContext; public Session getCurrentSession() throws HibernateException { if (this.currentSessionContext == null) { throw new HibernateException("No CurrentSessionContext configured!"); } return this.currentSessionContext.currentSession(); } } CurrentSessionContext接口裡面只有一個方法,currentSession() public abstract interface CurrentSessionContext extends Serializable { public abstract Session currentSession() throws HibernateException; } public class ThreadLocalSessionContext implements CurrentSessionContext { private static final ThreadLocal context = new ThreadLocal(); public final org.hibernate.classic.Session currentSession() throws HibernateException { org.hibernate.classic.Session current = existingSession(this.factory); if (current == null) { current = buildOrObtainSession(); current.getTransaction().registerSynchronization(buildCleanupSynch()); if (needsWrapping(current)) { current = wrap(current); } doBind(current, this.factory); } return current; } } </span>
Session與Transaction進行關聯是在底層session實現時操作的,你是否記得代理模式的作用,代理物件可以在客戶端和目標物件之間起到中介的作用,可以增加一些額外,這樣起到了保護目標物件的作用,在這裡事務正式通過代理模式給session新增事務的
如果只從ThreadLocalSessionContext的名稱上看,它所做的就是把一個session繫結到當前執行緒上,讓當前執行緒作為session的上下文。這樣,在service裡不同的dao通過sessionFactory.getCurrentSession()得到的將是當前執行緒上的同一個session,這是非常必要的做法,通過使用同一個session確保了持久化物件的一致性。但是從ThreadLocalSessionContext的程式碼來看,它並只是做了這一件事,它還做了另外一件非常“醒目”的事情:即給session包裹了一層代理org.hibernate.context.ThreadLocalSessionContext$TransactionProtectionWrapper,這個代理將攔截session的操作,對session的使用做出瞭如下的限制:
1.在沒有顯式地開始一個事務之前,不能使用session的任何資料訪問方法。
2.一旦事務提交,session將自動close。
通過上面這樣的限制就使得事務同session的週期變的一致了,由於session放在了一個ThreadLocal裡面,如此以來,threadlocal、session、transaction三者的生命週期是一致的,達到了對於同一個請求在同一個執行緒同一個事務中,session也是同一個session,既保證了持久化物件的一致性,又保證了事務的機制。
相反在提交事務之後,如果再從threadlocal中取得session將是一個新的session物件。因此,session的週期和事務是繫結在一起的。
ThreadLocal在這裡當做上下文來存放session物件,每次在用的時候只需要從這裡面取出來就可以了。