空間站

          北極心空

            BlogJava :: 首頁 :: 聯系 :: 聚合  :: 管理
            15 Posts :: 393 Stories :: 160 Comments :: 0 Trackbacks

          建議查看原帖:http://www.javaeye.com/topic/80697

          開發目的
          :一個協同平臺項目,多托管用戶,單門戶系統,每個托管用戶對應一個單一數據庫,要求根據登陸用戶的單位信息,自動選擇操作數據庫;同時,涉及跨庫操作(比如跨庫查詢,跨庫單據發送);同時事務處理必須支持這種多數據庫模式,支持一些邏輯性不強的跨庫事務,比如一些數據的發送和接收等

          當然,如果說跨庫操作只涉及到數據的發送和接受的話,也可以通過構建專門web service以及通信線程來處理,

          開發環境: tomcat4.1,webwork2.2.4,spring2.0.4,hibernate3.1,osworkflow2.8,mysql5.0.19 由于正式發布的應用服務器是weblogic8.1,所以沒有采用jdk5環境以及struts2

          準備:

          問題一由于有跨庫操作,而且這種跨庫操作無法預知,有的跨庫操作完全是在邏輯運行中決定的,比如A托管用戶或則C、D向B托管用戶發了訂單 ,B回復,這個回復是根據訂單發送者來說的,具體到后臺操作,是無法事先預知針對具體哪個后臺物理數據庫操作的.所以,也就是說,存在在業務執行過程中切換數據庫的情況,傳統的到注入設置dao類 sessionFactory、靠filter以及Interceptor設置線程安全的sessionFactory都無法完全達到設計目的

          問題二事務,本來,我打算用JtaTransactionManager的,除了JtaTransactionManager,在開始時也實在沒想到什么好的辦法, 難道,JtaTransactionManager是唯一選擇么?

          步驟:

          因為問題一,所以系統在資源方面是多sessionFactory并存的方式,也就是多少個托管用戶多少個sessionFactory,當然,事實上,這種應用型項目本身并發訪問量不會太高(什么,很高么,總比不過廣告聯盟吧,哈哈).不用擔心多了幾個sessionFactory會對系統或則數據庫造成多大影響.

          xml 代碼
           
          1. <bean id="mitDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">  
          2.       <property name="driverClass">  
          3.        <value>com.mysql.jdbc.Driver</value>  
          4.       </property>  
          5.       <property name="jdbcUrl">  
          6.        <value>jdbc:mysql://127.0.0.1:3306/mitflow</value>  
          7.       </property>  
          8.       <property name="user">  
          9.        <value>root</value>  
          10.       </property>  
          11.     ...........................  
          12.  </bean>  
          13.   
          14. <bean id="mitSessionFactory"  
          15.   class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">  
          16.   <property name="mappingResources">  
          17.         ..................................  
          18.     </props>  
          19.   </property>  
          20.     
          21.    <property name="dataSource">  
          22.      <ref local="mitDataSource" />  
          23.    </property>  
          24.  </bean>  

          然后復制,粘貼,修改jdbcUrl,象網站編輯一樣..

          假設,我配置了兩個sessionFatory ,一個是mitSessionFactory,一個是testSessionFactory,如下:

          xml 代碼
          1. <hibernatesupport>
          2. <itemid="mit"bean="mitSessionFactory"/>
          3. <itemid="test"bean="testSessionFactory"/>
          4. <hibernatesupport>

          這個我自己系統配置的一部分,系統會解析他,從而知曉究竟存在多少個sessionFactory,item's XmlNode中的id可以理解會托管客戶的客戶單位

          編號,當然,這個配置完全可以忽略,直接從ApplicationContext中一樣可以獲取到這樣的信息

          在客戶登陸的時候,系統要記錄下該客戶所屬托管單位,然后通過上面的id找到bean's name ,最后獲取這個sessionFactory,托管單位信息一般

          都是個編號而已跟己方系統的托管用戶管理相結合,一般是保存這個編號在session里面,也可以象asp.net一樣,記錄在安全憑證里,還不知道JAVA方面有沒有類似實現,個人認為asp.net這個方法很值得采用,雖然MS號稱安全系數+++++這個觀點值得懷疑

          首先建立一個類,HibernateSupport ,存放當前請求線程所需sessionFactory

          java 代碼
           
          1. public class HibernateSupport {  
          2.     public static final String HIBERNATE_SESSIONIDKEY = "com.mit.hibernatesupport.factory.id";  
          3.     private static final Logger logger = Logger.getLogger(HibernateSupport.class);  
          4.     private static ApplicationContext applicationContext ;  
          5.     private static boolean singleSession=true;  
          6.     private static Map factorybeanset;  
          7.     private static ThreadLocal switchhistory;//在切換不同sessionFactory的時候用到  
          8.     private static ThreadLocal idset;//記錄當前默認的托管用戶id,在實際使用中,這個是可以取消掉的  
          9.     private static ThreadLocal curfactory;////當前正在使用的sessionFactory  
          10.     private static ThreadLocal trace;//一個sessionFactory集合,用來記錄這次線程調用了那些sessionFactory  
          11.     static  
          12.     {  
          13.         idset = new ThreadLocal();  
          14.         curfactory = new ThreadLocal();  
          15.         trace = new ThreadLocal();  
          16.         switchhistory = new ThreadLocal();  
          17.     }  
          18.       
          19.     /** 
          20.      * set current sessionfactory for the Request 
          21.      * @param  ServletContext 
          22.      * @param  the factory's id defined in courser.xml 
          23.      */  
          24.     public static synchronized void setCurrent(ServletContext context,Object id)  
          25.     {  
          26.         if (idset.get()==null)  
          27.         {  
          28.             idset.set(id);  
          29.             if (factorybeanset.containsKey(id))  
          30.             {  
          31.                 if (applicationContext==null)  
          32.                 {  
          33.                      applicationContext =   
          34.                     WebApplicationContextUtils  
          35.                         .getWebApplicationContext(context);  
          36.                 }  
          37.                 curfactory.set((SessionFactory)applicationContext  
          38.                         .getBean((String)factorybeanset.get(id)));  
          39.                 putTrace(idset.get(),(SessionFactory)curfactory.get());  
          40.             }  
          41.         }  
          42.     }  
          43.       
          44.     /** 
          45.      * put the sessionfactory to tracemap 
          46.      * @see COPenSessionInViewFilter release sessionfactory in tracemap  
          47.      * @param  the factory's id defined in courser.xml 
          48.      * @param  hibernate's sessionfactory 
          49.      */  
          50.     private static void putTrace(Object id ,SessionFactory factory)  
          51.     {  
          52.         Map tracemap = null;  
          53.         if (trace.get()==null)  
          54.         {  
          55.             tracemap = new HashMap();  
          56.             trace.set(tracemap);  
          57.         }  
          58.         else  
          59.         {  
          60.             tracemap = (Map)trace.get();  
          61.         }  
          62.         if (!tracemap.containsKey(id))  
          63.         {  
          64.             tracemap.put(id, factory);  
          65.         }  
          66.     }  
          67.       
          68.     /** 
          69.      * switch current sessionfactory  
          70.      * @param  the factory's id defined in courser.xml 
          71.      */  
          72.     public static synchronized void swtichFactory(Object id)  
          73.     {  
          74.         if (!idset.get().equals(id)  )  
          75.         {  
          76.             if (factorybeanset.containsKey(id))  
          77.             {  
          78.                 SessionFactory oldfactory = (SessionFactory)curfactory.get();         
          79.                 SessionFactory newfactory = (SessionFactory)applicationContext  
          80.                 .getBean((String)factorybeanset.get(id));  
          81.                 curfactory.set(newfactory);  
          82.                 pushHistory(oldfactory);  
          83.                 putTrace(id,newfactory);  
          84.                 bindSessionFactory(newfactory);  
          85.             }  
          86.         }  
          87.     }  
          88.       
          89.     /** 
          90.      * restore sessionfactory from queue of switchhistory 
          91.      */  
          92.     public static synchronized void restoreFactory()  
          93.     {  
          94.         SessionFactory factory = popHistory();  
          95.         if (factory!=null)  
          96.         {  
          97.             curfactory.set(factory);  
          98.         }  
          99.     }  
          100.     /** 
          101.      * push old sessionfactory to swithhistory after swtichFactory 
          102.      * @param hibernate's sessionfactory 
          103.      */  
          104.     private static void pushHistory(SessionFactory sessionfactory)  
          105.     {  
          106.         LinkedList list = null;  
          107.         if (switchhistory.get()==null)  
          108.         {  
          109.             list = new LinkedList();  
          110.             switchhistory.set(list);  
          111.         }  
          112.         else  
          113.         {  
          114.             list = (LinkedList)switchhistory.get();  
          115.         }  
          116.         list.add(0,sessionfactory);  
          117.           
          118.     }  
          119.     /** 
          120.      * pop sessionfactory in queue 
          121.      */  
          122.     private static SessionFactory popHistory()  
          123.     {  
          124.         if (switchhistory.get()!=null)  
          125.         {  
          126.             LinkedList list = (LinkedList)switchhistory.get();  
          127.             if (list.size()>0)  
          128.             {  
          129.                 SessionFactory factory = (SessionFactory)list.getFirst();  
          130.                 list.removeFirst();  
          131.                 return factory;  
          132.             }  
          133.         }  
          134.         return null;  
          135.     }  
          136.       
          137.     public static Map getTraceMap()  
          138.     {  
          139.         if (trace.get()!=null)  
          140.         {  
          141.             return (Map)trace.get();  
          142.         }  
          143.         return null;  
          144.     }  
          145.       
          146.     public static SessionFactory getCurrentFactory()  
          147.     {  
          148.         return (SessionFactory)curfactory.get();  
          149.     }  
          150.       
          151.     public static synchronized void release()  
          152.     {  
          153.         idset.set(null);  
          154.         curfactory.set(null);  
          155.         switchhistory.set(null);  
          156.         trace.set(null);  
          157.     }  
          158.       
          159.     /** 
          160.      * °ó¶¨sessionFactoryµ½springµÄ×ÊÔ´¹ÜÀí 
          161.      * @param hibernate's sessionfactory 
          162.      */  
          163.     private static synchronized boolean bindSessionFactory(SessionFactory sessionFactory)  
          164.     {  
          165.         boolean participate=false;;  
          166.         if (singleSession) {  
          167.             // single session mode  
          168.             if (TransactionSynchronizationManager.hasResource(sessionFactory)) {  
          169.                 // Do not modify the Session: just set the participate flag.  
          170.                 participate = true;  
          171.             }  
          172.             else {  
          173.                 logger.debug("Opening single Hibernate Session in OpenSessionInViewFilter");  
          174.                 Session session = getSession(sessionFactory);  
          175.                 if (!TransactionSynchronizationManager.hasResource(sessionFactory))  
          176.                 {     
          177.                     TransactionSynchronizationManager.bindResource(sessionFactory, new SessionHolder(session));  
          178.                 }  
          179.             }  
          180.         }  
          181.         else {  
          182.             // deferred close mode  
          183.             if (SessionFactoryUtils.isDeferredCloseActive(sessionFactory)) {  
          184.                 // Do not modify deferred close: just set the participate flag.  
          185.                 participate = true;  
          186.             }  
          187.             else {  
          188.                 SessionFactoryUtils.initDeferredClose(sessionFactory);  
          189.             }  
          190.         }  
          191.         return participate;  
          192.     }  
          193.       
          194.     //see SessionFactoryUtils  
          195.     private static  Session getSession(SessionFactory sessionFactory) throws DataAccessResourceFailureException {  
          196.         Session session = SessionFactoryUtils.getSession(sessionFactory, true);  
          197.         FlushMode flushMode = FlushMode.COMMIT;  
          198.         if (flushMode != null) {  
          199.             session.setFlushMode(flushMode);  
          200.         }  
          201.         return session;  
          202.     }  
          203.   
          204.     public static synchronized void initSessionFactory(Map res,Class loadclass)  
          205.     {  
          206.         factorybeanset =res;  
          207.     }  
          208.   
          209.       
          210. }  
          HibernateSupport這個類其他方法可以不管,暫時關注setCurrent這個方法

          java 代碼
          1. if (idset.get()==null)
          2. {
          3. idset.set(id);
          4. if (factorybeanset.containsKey(id)) //factorybeanset包含的就是我自己系統配置中那一部分,key就是id,,value就是sessionFactory 在spring環境中的beanName
          5. {
          6. if (applicationContext==null)
          7. {
          8. applicationContext =WebApplicationContextUtils.getWebApplicationContext(context);
          9. }
          10. curfactory.set((SessionFactory)applicationContext.getBean((String)factorybeanset.get(id)));//設置當前的sessionFactory
          11. putTrace(idset.get(),(SessionFactory)curfactory.get());//put到當前線程的一個記錄集
          12. }
          13. }
          然后,就要修改spring關于hibernate的一些支持類了,當然,也可以選擇重新寫一套dao支持類,呵呵,不過,顯然,在spring基礎上做一些小修改代價更小 HibernateAccessorHibernateTemplate的基類)以及HibernateTransactionManager都是靠注入方式獲取一個sessionFactory,顯然,這套不適合了,修改之

          sessionFactory好做,配置在spring或則單獨拿出來處理都可以,但是spring的HibernateDaoSupport 必須綁定一個sessionFactory,當然,我們完全可以寫一個自己的HibernateDaoSupport ,但是既然用了spring的事務管理而又不想多花時間,還是將就改改用吧

          java 代碼
          1. private SessionFactory sessionFactory;
          2. publicvoid setSessionFactory(SessionFactory sessionFactory) {
          3. this.sessionFactory = sessionFactory;
          4. }

          去掉HibernateAccessorHibernateTransactionManager中的上述兩段代碼,當然,也別忘了斃掉兩個類中的

          afterPropertiesSet方法中那些檢查代碼

          然后 ,ant打包就可以了,如果不想修改spring的代碼,也可以單獨把這幾個類提出來另建jar,我是單獨提出來新建的,比如HibernateTransactionManager我改名成CHibernateTransactionManager,其他類似,但是包名必須是org.springframework.orm.hibernate3 ,誰也不想這么做,可是誰讓sessionFactoryUtils中一個closexxxx方法沒定義成public了??

          如果想變更sessionFactoryUtils,奉勸算了吧..

          然后可以做測試了,首先,部署測試的daoservice,先是事務部署

          xml 代碼
          1. <beanid="transactionManager"
          2. class="com.mit.web.hibernate.CHibernateTransactionManager"/>
          3. <beanid="transres"class="org.springframework.transaction.interceptor.NameMatchTransactionAttributeSource">
          4. <propertyname="properties">
          5. <props>
          6. <propkey="load*">PROPAGATION_REQUIRED,readOnlyprop>
          7. <propkey="save*">PROPAGATION_REQUIREDprop>
          8. <propkey="delete*">PROPAGATION_REQUIREDprop>
          9. <propkey="find*">PROPAGATION_REQUIRED,readOnlyprop>
          10. <propkey="query*">PROPAGATION_REQUIRED,readOnlyprop>
          11. <propkey="create*">PROPAGATION_REQUIREDprop>
          12. <propkey="set*">PROPAGATION_REQUIRED,readOnlyprop>
          13. <propkey="execute*">PROPAGATION_REQUIREDprop>
          14. props>
          15. property>
          16. bean>
          17. <beanid="transactionInterceptor"class=span
          家里網絡環境不好,本來想修改下里面的代碼格式,結果成了半截子,我轉到回復里面來寫把3
          事務配置如下:
          Java代碼 復制代碼
          1.  <bean id="transactionManager"     
          2.          class="com.mit.web.hibernate.CHibernateTransactionManager"/>   
          3.   
          4. <bean id="transres" class="org.springframework.transaction.interceptor.NameMatchTransactionAttributeSource">   
          5.     <property name="properties">   
          6.         <props>   
          7.             <prop key="load*">PROPAGATION_REQUIRED,readOnly</prop>   
          8.             <prop key="save*">PROPAGATION_REQUIRED</prop>   
          9.             <prop key="delete*">PROPAGATION_REQUIRED</prop>   
          10.             <prop key="find*">PROPAGATION_REQUIRED,readOnly</prop>   
          11.             <prop key="query*">PROPAGATION_REQUIRED,readOnly</prop>   
          12.             <prop key="create*">PROPAGATION_REQUIRED</prop>   
          13.             <prop key="set*">PROPAGATION_REQUIRED,readOnly</prop>   
          14.             <prop key="execute*">PROPAGATION_REQUIRED</prop>   
          15.         </props>   
          16.     </property>   
          17. </bean>   
          18.   
          19. <bean id="transactionInterceptor" class="org.springframework.transaction.interceptor.TransactionInterceptor">     
          20.        <property name="transactionManager" ref="transactionManager"/>     
          21.      <property name="transactionAttributeSource"><ref local="transres"/></property>   
          22.    </bean>     
          23.      
          24.    <bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">     
          25.  <property name="beanNames"><value>*Service</value></property>   
          26.        <property name="interceptorNames">     
          27.            <list>     
          28.                <value>transactionInterceptor</value>     
          29.            </list>     
          30.        </property>     
          31.    </bean>     
          32.     
          33.    <bean class="org.springframework.transaction.interceptor.TransactionAttributeSourceAdvisor">     
          34.      <property name="transactionInterceptor" ref="transactionInterceptor"/>     
          35.    </bean>   

          我把事務配置在service層,事務管理采用的是我修改后的HibernateTransactionManager ,他不在要求sessionFactory的注入了
          以下是測試的dao類和service類配置
          Java代碼 復制代碼
          1. <bean id="personDao" class="com.mit.web.action.PersonDaoImpl"/>    
          2. <bean id="personService" class="com.mit.web.action.PersonServiceImpl">    
          3.     <property name="personDao">    
          4.      <ref bean="personDao"/>    
          5.     </property>    
          6.  </bean>    
          7.     
          8. <bean id="departDao" class="com.mit.web.action.DepartDaoImpl"/>    
          9. <bean id="departService" class="com.mit.web.action.DepartServiceImpl">    
          10.     <property name="personService">    
          11.         <ref bean="personService"/>    
          12.     </property>    
          13.     <property name="departDao">    
          14.         <ref bean="departDao"/>    
          15.     </property>    
          16. </bean>   

          具體代碼不寫了,大概如下:
          Java代碼 復制代碼
          1. public class PersonDaoImpl extends CHibernateDaoSupport implements PersonDao;    
          2. public class PersonServiceImpl implements PersonService;    
          3. public class DepartDaoImpl extends CHibernateDaoSupport implements DepartDao;    
          4. public class DepartServiceImpl implements DepartService;   

          測試代碼的目的是將兩個實體類分別存放到mit和test庫,假設默認用戶是mit這個托管庫的,這個方法就寫在DepartServiceImpl類里面
          Java代碼 復制代碼
          1. public class DepartServiceImpl implements DepartService {    
          2. private DepartDao dao;    
          3. private PersonService service;    
          4.  ..............................    
          5.  public void executeTest(Depart depart,Person person)    
          6.  {    
          7.     dao.save(depart);    
          8.     HibernateSupport.switchFactory("test");    
          9.     service.save(person);    
          10.     HibernateSupport.restoreFactory();    
          11. }    
          12.  ..............................   

          上面代碼中將depart對象存到mit庫,而將person對象存到test庫中.

          事務 事務 ,沒看見特別事務設置? 其實事務已經在運行了

          如前面配置,事務我配置在了service層,那么當executeTest執行時,會啟動針對mit庫的事務(前面我們改了HibernateTransactionManager的sessionFactory獲取方式),

          dao.save(depart);//然后保存depart到mit庫

          HibernateSupport.switchFactory("test");//切換庫到test

          service.save(person);//調用personService的save方法,這也是個service,所以也會產生一個事務,而此時HibernateSupport.getCurrentFactory返回的sessionFactory已經是test庫的了,所以,spring事務管理發現上下文中并沒有針對test的事務,于是,會重新啟動一個新的事務,這個service.save(person);方法執行完后,這個事務將會提交

          HibernateSupport.restoreFactory();//切換回默認數據庫

          整個方法執行完后,針對mit的事務將會被提交

          [color=green]如果service.save(person);發生異常,這兩個事務就會被提交,一個簡單跨庫事務控制完成[color=green]

          但是,問題也隨之而來,如果在 HibernateSupport.restoreFactory();后,又進行了一些邏輯操作,那么發生異常時,而由于 service.save(person);這個事務已經被提交,也就是說,針對test的事務已經提交不會回滾了,這是個非常嚴重的問題。。

          其實,也有解決方法,就是延遲提交,具體實現方式以后講述

          跨庫事務之延遲提交
          延遲事務,就是將事務延后提交,延遲的時間由事務管理器掌握。在我的系統中,只有跨庫操作涉及到延遲提交,針對這種操作,我設計了一個基本的執行模型。就是如果一個邏輯中存在多個事務,將全部放到邏輯執行完以后提交,那么,既然如此,開始吧
          PlatformTransactionManager是spring平臺相關事務,比如HibernateTransactionManager都是繼承于此類,為了達到延遲提交的目的,可以在AbstractPlatformTransactionManagershang上做修改達到目的
          首先,說一下在spring中,通常的一個事務流程,
          流程如下:初始化Transaction B,如果發現前面有其他Transaction ,比如 Transaction A,那么掛起TransactionA ,然后啟動事務 ,當邏輯執行完后 ,commit,恢復掛起事務A,然后清除同步對象以及其他資源,如果執行發生異常,當然,異常發生后 rollback
          延遲提交的設計思想是將事務都暫存在一個threadlocal的LIST里面,等邏輯執行完以后,再統一提交,那么首先在AbstractPlatformTransactionManager中設置一個threadlocal對象
          Java代碼 復制代碼
          1. private ThreadLocal lazytrace = new ThreadLocal();  

          LazyTransactionStatus用來管理需要延遲提交的事務
          Java代碼 復制代碼
          1. private static class LazyTransactionStatus   
          2. {   
          3.     private java.util.List transliat;   
          4.     private int transnum;//代表未提交事務數量   
          5.        
          6.     public LazyTransactionStatus()   
          7.     {   
          8.         transliat= new java.util.ArrayList();   
          9.         transnum=0;   
          10.     }   
          11.        
          12.     public void newLazy()   
          13.     {   
          14.         transnum++;   
          15.     }   
          16.        
          17.     public void push(TransactionStatus trasobj)   
          18.     {      
          19.         objmap.add(trasobj);   
          20.     }   
          21.        
          22.     public void removeLazy(TransactionStatus trasobj)   
          23.     {   
          24.         transnum--;   
          25.     }   
          26.        
          27.     public boolean canCommit()   
          28.     {   
          29.         if (transnum<1)   
          30.         {   
          31.             return true;   
          32.         }   
          33.         else  
          34.             return false;   
          35.     }   
          36.        
          37.     public java.util.List getTransactionObjs()   
          38.     {   
          39.         return transliat;   
          40.     }   
          41. }   

          這就是local對象的存儲內容.translist存放當前執行中的TransactionStatus實例
          TransactionStatus顧名思義,事務狀態,包含了事務、掛起資源等一系列信息
          然后以事務提交為例
          然后在AbstractTransactionManager增加如下兩個方法
          Java代碼 復制代碼
          1. public final boolean isCommit(TransactionStatus status)   
          2. {   
          3.     if (lazytrace.get()!=null)   
          4.     {   
          5.         LazyTransactionStatus lazystatus = (LazyTransactionStatus)lazytrace.get();   
          6.         lazystatus.removeLazy(status);   
          7.         return lazy.canCommit();   
          8.     }   
          9.     return true;   
          10. }   
          11.   
          12. protected void begin(Object transaction, TransactionDefinition definition)   
          13. {   
          14.     doBegin(transaction,definition);   
          15.     LazyTransactionStatus lazystatus = null;   
          16.     if (lazytrace.get()==null)   
          17.     {   
          18.         lazystatus = new LazyTransactionStatus();   
          19.         lazytrace.set(lazystatus);   
          20.     }   
          21.     else  
          22.     {   
          23.         lazystatus = (LazyTransactionStatus)lazytrace.get();   
          24.     }   
          25.     lazystatus.newLazy();   
          26. }   
          27.   
          28. public final void registerTraceStatus(TransactionStatus status)   
          29. {   
          30.     LazyTransactionStatus lazystatus = null;   
          31.     if (lazytrace.get()==null)   
          32.     {   
          33.         lazystatus = new LazyTransactionStatus();   
          34.         lazytrace.set(lazystatus);   
          35.     }   
          36.     else  
          37.     {   
          38.         lazystatus = (LazyTransactionStatus)lazytrace.get();   
          39.     }   
          40.     lazystatus.push(status);   
          41. }  

          begin ,當一個事務開始時,將LazyTransactionStatus的transnum+1,表示又多了個剛開始,還未提交的事務
          registerTraceStatus發生在commit的時候,注冊這個事務到LazyTransactionStatus,同時,
          注意 transnum表示的是未提交事務數量,所以當事務管理器執行commit表示要提交一個事務后,transnum將減一,如果減一后發現transnum<1,表示所有事務都提交了,那么,將所有事務提交。否則,不提交,繼續等待...
          關系如下:
          begin->transnum+1 表示新事務誕生
          registerTraceStatus(發生在commit的時候)->將準備提交的TransStatus放到LazyTransactionStatus,是的,這個事務要提交了,來吧,先注冊一下
          緊接著
          isCommit()->將transnum-1,如果發現transnum小于1 ,表示鬧夠了,可以都提交滾蛋了
          注意 ,transnum與LazyTransactionStatus的translist的鏈表長度在執行commit的時候是反方向發展的 一個增,一個減
          好了,首先是注冊事務數量,不用管了,在事務開始時begin方法它自己會調用了,
          然后是修改AbstractPlatformTransactionManager的commit方法

          Java代碼 復制代碼
          1. public final void commit(TransactionStatus txstatus) throws TransactionException {   
          2.     this.registerTraceStatus(txstatus);   
          3.     if (this.isCommit(txstatus))   
          4.     {   
          5.         int error = 0;   
          6.         LazyTransactionStatus lazystatus = (LazyTransactionStatus)lazytrace.get();   
          7.         List statuslist = lazystatus.getTransactionObjs();   
          8.         for (int i=0;i<statuslist.size();i++)   
          9.         {   
          10.             try  
          11.             {   
          12.                 TransactionStatus status = (TransactionStatus)statuslist.get(i);   
          13.                 if (status.isCompleted()) {   
          14.                     error++;   
          15.                     continue;   
          16.                     //throw new IllegalTransactionStateException(   
          17.                     //      "Transaction is already completed - do not call commit or rollback more than once per transaction");   
          18.                 }   
          19.            
          20.                 DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;   
          21.                 if (defStatus.isLocalRollbackOnly()) {   
          22.                     if (defStatus.isDebug()) {   
          23.                         logger.debug("Transactional code has requested rollback");   
          24.                     }   
          25.                     processRollback(defStatus);   
          26.                     error++;   
          27.                     continue;   
          28.                 }   
          29.                 if (!shouldCommitOnGlobalRollbackOnly() && defStatus.isGlobalRollbackOnly()) {   
          30.                     if (defStatus.isDebug()) {   
          31.                         logger.debug("Global transaction is marked as rollback-only but transactional code requested commit");   
          32.                     }   
          33.                     processRollback(defStatus);   
          34.                     // Throw UnexpectedRollbackException only at outermost transaction boundary   
          35.                     // or if explicitly asked to.   
          36.                     if (status.isNewTransaction() || isFailEarlyOnGlobalRollbackOnly()) {   
          37.                         //throw new UnexpectedRollbackException(   
          38.                                 //"Transaction rolled back because it has been marked as rollback-only");   
          39.                         error++;   
          40.                         continue;   
          41.                     }   
          42.                     continue;   
          43.                 }   
          44.                    
          45.                 processCommit(defStatus);   
          46.             }   
          47.             catch (Exception ex)   
          48.             {   
          49.                 error++;   
          50.                 ex.printStackTrace();   
          51.                 continue;   
          52.             }   
          53.                
          54.         }   
          55.         lazytrace.set(null);   
          56.         if (error>0)   
          57.             throw new IllegalTransactionStateException(   
          58.                         "Not commit all transaction");   
          59.     }   
          60. }  

          this.registerTraceStatus(txstatus);//事務提交了,成了嫌疑犯,拖到threadlocal的LazyTransactionStatus監獄里面先關起來
          if (isCommit()) //看看監獄的事務是不是滿了,如果滿了,就可以全放了
          LazyTransactionStatus lazystatus = (LazyTransactionStatus)lazytrace.get();
          List statuslist = lazystatus.getTransactionObjs();
          for (int i=0;i<statuslist.size();i++)
          {
                                    ........
                                   processCommit(defStatus);
          //看來真的滿了,都放了吧
          回滾道理是一樣的,不過不用判斷了,直接全部放出來讓他們滾吧
          當然,目前這個實現只是個模型,真要實際應用,還需要做進一步封裝,實際做,我用了OpenSessionInViewFilter,我已經做過測試,測試了了OpenSessionInViewFilter中singleSession為true和false兩種情況,測試都通過了,呵呵
          posted on 2008-11-13 11:25 蘆葦 閱讀(5767) 評論(0)  編輯  收藏 所屬分類: Hibernate
          主站蜘蛛池模板: 垫江县| 泰安市| 延庆县| 南部县| 玉龙| 丹寨县| 湘潭县| 织金县| 敦化市| 同德县| 武义县| 乐平市| 瑞丽市| 轮台县| 浑源县| 漾濞| 芮城县| 永吉县| 临漳县| 元江| 砚山县| 淮阳县| 武汉市| 江门市| 赤峰市| 手机| 贺兰县| 凌云县| 黑龙江省| 定兴县| 新安县| 福鼎市| 铜川市| 仙居县| 临洮县| 舞钢市| 独山县| 兰溪市| 吉首市| 桑日县| 洱源县|