Spring HibernateTransactionManager事物管理類(HibernateTransactionObject)

來源:互聯網
上載者:User

我們在Spring裡通常把事務交給HibernateTransactionManager來處理,通過Aop配置事務,把事務交給該類之後就會由該類來幫我們管理和提供事務。這裡它是怎麼實現的,下面我們深入源碼.....

public class HibernateTransactionManager extends AbstractPlatformTransactionManagerimplements ResourceTransactionManager, BeanFactoryAware, InitializingBean {

首先該類繼承了AbstractPlatformTransactionManager類且實現了一系列介面,

public abstract class AbstractPlatformTransactionManager implements PlatformTransactionManager, Serializable {

AbstractPlatformTransactionManager這個抽象類別實現了介面的getTransaction(TransactionDefinition definition)方法,該方法中執行了doGetTransaction()、doBegin()方法以及其它方法,這裡就不一一介紹了,

回到子類HibernateTransactionManager類,該類的重寫了父類的doGetTransaction()方法和doBegin(),doCommit(),所以這個方法才是我們主要要討論的,還有doCommit().........

從父類的getTransaction(TransactionDefinition definition)可以看出會先走doGetTransaction(),而後在走doBegin(),

我們來看doGetTransaction()方法源碼:

protected Object doGetTransaction() {HibernateTransactionObject txObject = new HibernateTransactionObject();//這裡new一個HibernateTransactionObject對象txObject.setSavepointAllowed(isNestedTransactionAllowed());//設定一個儲存點               //從當前線程當中以sessionFacoty為key去取相對應的sessionHolder,SessionHolder sessionHolder =(SessionHolder) TransactionSynchronizationManager.getResource(getSessionFactory());if (sessionHolder != null) {if (logger.isDebugEnabled()) {logger.debug("Found thread-bound Session [" +SessionFactoryUtils.toString(sessionHolder.getSession()) + "] for Hibernate transaction");}txObject.setSessionHolder(sessionHolder);//把sessionHolder設定到txObject當中}else if (this.hibernateManagedSession) {//這是判斷有沒有設定在當前上下文,比如在設定檔中的thread,或Spring上下文try {Session session = getSessionFactory().getCurrentSession();//有就直接從當前上下文去取if (logger.isDebugEnabled()) {logger.debug("Found Hibernate-managed Session [" +SessionFactoryUtils.toString(session) + "] for Spring-managed transaction");}txObject.setExistingSession(session);//和上面一樣設定sessionHolder到txObject當中,該set方法中又把session封裝了下}catch (HibernateException ex) {throw new DataAccessResourceFailureException("Could not obtain Hibernate-managed Session for Spring-managed transaction", ex);}}               //在事務對象中設定DataSource,其中有個afterPropertiesSet()將從sessionFactory中擷取DataSourceif (getDataSource() != null) {ConnectionHolder conHolder = (ConnectionHolder)TransactionSynchronizationManager.getResource(getDataSource());//從當前線程中擷取繫結資料庫串連,它是在doBegin()方法綁定的txObject.setConnectionHolder(conHolder);//把從線程中取得的sessionHolder設定到txObject中}return txObject;}

上面這個doGetTransaction()方法走完了就建立了HibernateTransactionObject txObject對象,這個對象也是主角,且往這個對象中填充了兩個屬性,為這兩個屬性賦好值,一個是sessionholder,另一個是connectionHolder,也就是session和connect。

當你在商務邏輯裡面的C方法裡麵包含A,B方法時同時調用,只開了一個事務,session還是當前線程裡面的同一個,直接跑sessionFactoryUtils.dogetSession()。

分開在action調用時,先A後B,會為A開個事務,在為B開事務,但是從線程裡面取session。

下一個就是doBegin()方法,引入一個新對象TransactionDefinition事物描述,有些代碼省略.....

protected void doBegin(Object transaction, TransactionDefinition definition) {HibernateTransactionObject txObject = (HibernateTransactionObject) transaction;//取得txObject對象,把事務強轉HibernateTransactionObject               // 如果sessionHolder沒有建立,那麼這裡將會建立hibernate裡面的session,並把這個session放到SessionHolder中Session session = null;try {//判斷txObject上的sessionHolder值是否為空白if (txObject.getSessionHolder() == null || txObject.getSessionHolder().isSynchronizedWithTransaction()) {Interceptor entityInterceptor = getEntityInterceptor();//一個實體攔截器Session newSession = (entityInterceptor != null ?getSessionFactory().openSession(entityInterceptor) : getSessionFactory().openSession());if (logger.isDebugEnabled()) {logger.debug("Opened new Session [" + SessionFactoryUtils.toString(newSession) +"] for Hibernate transaction");}txObject.setSession(newSession);}                       //這裡從sessionHolder中取出session,為hibernateTransaction做準備session = txObject.getSessionHolder().getSession();if (this.prepareConnection && isSameConnectionForEntireSession(session)) {// We're allowed to change the transaction settings of the JDBC Connection.if (logger.isDebugEnabled()) {logger.debug("Preparing JDBC Connection of Hibernate Session [" + SessionFactoryUtils.toString(session) + "]");}Connection con = session.connection();Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition);txObject.setPreviousIsolationLevel(previousIsolationLevel);}else {  //這裡是設定Aop裡面你配置的isolation屬性// Not allowed to change the transaction settings of the JDBC Connection.if (definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT) {// We should set a specific isolation level but are not allowed to...throw new InvalidIsolationLevelException("HibernateTransactionManager is not allowed to support custom isolation levels: " +"make sure that its 'prepareConnection' flag is on (the default) and that the " +"Hibernate connection release mode is set to 'on_close' (SpringTransactionFactory's default). " +"Make sure that your LocalSessionFactoryBean actually uses SpringTransactionFactory: Your " +"Hibernate properties should *not* include a 'hibernate.transaction.factory_class' property!");}if (logger.isDebugEnabled()) {logger.debug("Not preparing JDBC Connection of Hibernate Session [" + SessionFactoryUtils.toString(session) + "]");}} //這裡是設定Aop裡面你配置的read-only屬性, if (definition.isReadOnly() && txObject.isNewSession()) { // Just set to NEVER in case of a new Session for this transaction. session.setFlushMode(FlushMode.MANUAL); } if (!definition.isReadOnly() && !txObject.isNewSession()) {//判斷事物是否唯讀,是否是一個新的session,也就是當前線程裡面存不存在session,不存在則為true(OpenSessionView)// We need AUTO or COMMIT for a non-read-only transaction.FlushMode flushMode = session.getFlushMode();if (flushMode.lessThan(FlushMode.COMMIT)) {session.setFlushMode(FlushMode.AUTO);txObject.getSessionHolder().setPreviousFlushMode(flushMode);}}Transaction hibTx;// Register transaction timeout.int timeout = determineTimeout(definition);if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {hibTx = session.getTransaction();//設定一個事務逾時機制,設定時間hibTx.begin();hibTx.setTimeout(timeout);}else {//將hibernate的事務設定到txObject的sessionHolder的裡面,這個sessionHolder會和線程綁定.hibTx = session.beginTransaction();} //將Transaction hibTx設定到txObject中,給txObject事務賦值,主要是一個已經開啟的事務   txObject.getSessionHolder().setTransaction(hibTx);// Register the Hibernate Session's JDBC Connection for the DataSource, if set.if (getDataSource() != null) {         Connection con = session.connection();         ConnectionHolder conHolder = new ConnectionHolder(con);//封裝connection     if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {              conHolder.setTimeoutInSeconds(timeout);      }     if (logger.isDebugEnabled()) {     logger.debug("Exposing Hibernate transaction as JDBC transaction [" + con + "]");     }     //把當前的資料庫connection綁定在當前線程當中.     TransactionSynchronizationManager.bindResource(getDataSource(), conHolder);     //在這裡給txObject的ConnectionHolder賦值,以保證在con不為null     txObject.setConnectionHolder(conHolder); }//如果是新的sessionHolder,將它和當前線程綁定// Bind the session holder to the thread.if (txObject.isNewSessionHolder()) {//判斷在當前線程裡面有沒有這個sessionHolder,當前裡面有則為false,open則是trueTransactionSynchronizationManager.bindResource(getSessionFactory(), txObject.getSessionHolder());//綁定到當前線程當中}//在sessionHolder中進行狀態標誌,標識事務已經開始。txObject.getSessionHolder().setSynchronizedWithTransaction(true);}}


上面這個doBegin方法,首先是得到一個在上面doGetTransaction()裡面建立的hibernateTransactionObject對象txObject,還有個實體攔截器,entityInterceptor,該攔截器的作用就相當於一個代理,要訪問被代理的對象,先走這個攔截器,我們在doGetTransaction方法中,先判斷了線上程和當前上下文能不能取得到session,並把其設定到txObject中,並且還在doBegin()中為了保證sessionHolder不為null,判斷同學txObject.getSessionHolder()如果為null,則通過sessionfactory開啟一個session,並把它傳到txObject封裝成sessionHolder,且在opensession方法中傳入攔截器,在開啟session之前做事.

設定isolation,

read-only='true'時,虛擬事務,可以保證hibernate查詢立刻發送sql語句.

1.走完doBegin(),txObject裡面設定session和connect,在這個方法已經保證這兩個值不為null,在doGetTrainsaction()方法是從線程裡面取,如果沒綁定,也就是null,在doBegin()中,保證了不為空白,而session分為3中情況,1.OpensessionView,2.getCurrentSession(可以是thead或是spring上下文),3.opensession

上面原始碼中的isNewSessionHolder()方法是返回這個session是否是一個新的,像OpensessionView則返回false,其它兩中情況則是返回true,是新的則要綁定到線程當中.

2.得到的session在方法中開啟了一個事務,並把事務存放到sessionHolder裡,由於sessionHolder是綁定到線程當中,所以它的事務也將會同步

3.其中,還把資料庫連接綁定到當前線程中去了,以資料庫連接池datasouce為key,value是connectHolder.

總:doBegin()走完後,當前線程中就已經有兩對值:

key value
sessionFactory sessionHolder
dataSource conHolder

4.現在這個主角類HibernateTransactionObject對象txObject已經包含了sessionHolder,conHolder,已經開啟了的Transaction。5.從doBegin()方法裡面的判斷sessionHolder是否為null,我們可以看出一個線程對應一個資源,保證一個線程裡面只有一個session,一個connect,不能重複,因為線程裡面的Map的key是唯一的,

Thread.currentThread——>t

t.threadLocals這個Map中包含的{key:ThreadLocal<Map>,value:Map},這裡面的Map存放著session,connect。

protected void doCommit(DefaultTransactionStatus status) {//事務提交HibernateTransactionObject txObject = (HibernateTransactionObject) status.getTransaction();if (status.isDebug()) {logger.debug("Committing Hibernate transaction on Session [" +SessionFactoryUtils.toString(txObject.getSessionHolder().getSession()) + "]");}try {txObject.getSessionHolder().getTransaction().commit();//得到sessionHolder的事務,直接提交 }

doCommit()眾所周知是事務提交,當然在提交之前,也做了些許多事情,比如清空session,這些方法就沒一一列出.可以查看父類AbstractPlatformTransactionManager中的processCommit()方法, 可以看到是先跑一些prepareForCommit準備提交的方法等等......

綁定到線程上的事務,rollback.

更多詳細查看 :

http://sailinglee.iteye.com/blog/598908

http://books.google.com.hk/books?id=jRVp2INtY1AC&pg=PA222&lpg=PA222&dq=HibernateTransactionManager#v=onepage&q=HibernateTransactionManager&f=false

TransactionDefinition:

http://book.51cto.com/art/200909/149403.htm

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.