<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    內(nèi)蒙古java團(tuán)隊(duì)

    j2se,j2ee開發(fā)組
    posts - 139, comments - 212, trackbacks - 0, articles - 65
      BlogJava :: 首頁 :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理

    事務(wù)處理

    Posted on 2007-12-20 11:42 帥子 閱讀(601) 評論(1)  編輯  收藏 所屬分類: J2EE技術(shù)專區(qū)
    事務(wù)處理是企業(yè)應(yīng)用需要解決的最主要的問題之一。J2EE通過JTA提供了完整的事務(wù)管理能力,包括多個(gè)事務(wù)性資源的管理能力。但是大部分應(yīng)用都是運(yùn)行在單一的事務(wù)性資源之上(一個(gè)數(shù)據(jù)庫),他們并不需要全局性的事務(wù)服務(wù)。本地事務(wù)服務(wù)已然足夠(比如JDBC事務(wù)管理)。
        本文并不討論應(yīng)該采用何種事務(wù)處理方式,主要目的是討論如何更為優(yōu)雅地設(shè)計(jì)事務(wù)服務(wù)。僅以JDBC事務(wù)處理為例。涉及到的DAO,F(xiàn)actory,Proxy,Decorator等模式概念,請閱讀相關(guān)資料。
        也許你聽說過,事務(wù)處理應(yīng)該做在service層,也許你也正這樣做,但是否知道為什么這樣做?為什么不放在DAO層做事務(wù)處理。顯而易見的原因是業(yè)務(wù)層接口的每一個(gè)方法有時(shí)候都是一個(gè)業(yè)務(wù)用例(User Case),它需要調(diào)用不同的DAO對象來完成一個(gè)業(yè)務(wù)方法。比如簡單地以網(wǎng)上書店購書最后的確定定單為例,業(yè)務(wù)方法首先是調(diào)用BookDAO對象(一般是通過DAO工廠產(chǎn)生),BookDAO判斷是否還有庫存余量,取得該書的價(jià)格信息等,然后調(diào)用CustomerDAO從帳戶扣除相應(yīng)的費(fèi)用以及記錄信息,然后是其他服務(wù)(通知管理員等)。簡化業(yè)務(wù)流程大概如此:
        注意,我們的例子忽略了連接的處理,只要保證同一個(gè)線程內(nèi)取的是相同的連接即可(可用ThreadLocal實(shí)現(xiàn)): 



        首先是業(yè)務(wù)接口,針對接口,而不是針對類編程:


    public interface BookStoreManager{ 
              public boolean buyBook(String bookId,int quantity)throws SystemException; 
              ....其他業(yè)務(wù)方法 
        } 

        接下來就是業(yè)務(wù)接口的實(shí)現(xiàn)類??業(yè)務(wù)對象:


    public class BookStoreManagerImpl implements BookStoreManager{ 
             public boolean buyBook(String bookId)throws SystemException{ 
                  Connection conn=ConnectionManager.getConnection();//獲取數(shù)據(jù)庫連接 
                  boolean b=false; 
                  
                  try{ 
                      conn.setAutoCommit(false);  //取消自動(dòng)提交 
                      BookDAO bookDAO=DAOFactory.getBookDAO(); 
                      CustomerDAO customerDAO=DAOFactory.getCustomerDAO(); 
                        //嘗試從庫存中取書 
                      if(BookDAO.reduceInventory(conn,bookId,quantity)){ 
                           BigDecimal price=BookDAO.getPrice(bookId);  //取價(jià)格 
                           //從客戶帳戶中扣除price*quantity的費(fèi)用 
                           b= 
                           CustomerDAO.reduceAccount(conn,price.multiply(new BigDecimal(quantity)); 
                           .... 
                           其他業(yè)務(wù)方法,如通知管理員,生成定單等. 
                            ... 
                           conn.commit();   //提交事務(wù) 
                           conn.setAutoCommit(true); 
                      } 
                   }catch(SQLException e){ 
                      conn.rollback();   //出現(xiàn)異常,回滾事務(wù) 
                      con.setAutoCommit(true); 
                      e.printStackTrace(); 
                      throws new SystemException(e);   
                   } 
                   return b; 
             } 
        } 


        然后是業(yè)務(wù)代表工廠:  


      public final class ManagerFactory { 
          public static BookStoreManager getBookStoreManager() { 
             return new BookStoreManagerImpl(); 
          } 
       } 

        這樣的設(shè)計(jì)非常適合于DAO中的簡單活動(dòng),我們項(xiàng)目中的一個(gè)小系統(tǒng)也是采用這樣的設(shè)計(jì)方案,但是它不適合于更大規(guī)模的應(yīng)用。首先,你有沒有聞到代碼重復(fù)的 bad smell?每次都要設(shè)置AutoCommit為false,然后提交,出現(xiàn)異常回滾,包裝異常拋到上層,寫多了不煩才怪,那能不能消除呢?其次,業(yè)務(wù)代表對象現(xiàn)在知道它內(nèi)部事務(wù)管理的所有的細(xì)節(jié),這與我們設(shè)計(jì)業(yè)務(wù)代表對象的初衷不符。對于業(yè)務(wù)代表對象來說,了解一個(gè)與事務(wù)有關(guān)的業(yè)務(wù)約束是相當(dāng)恰當(dāng)?shù)模亲屗?fù)責(zé)來實(shí)現(xiàn)它們就不太恰當(dāng)了。再次,你是否想過嵌套業(yè)務(wù)對象的場景?業(yè)務(wù)代表對象之間的互相調(diào)用,層層嵌套,此時(shí)你又如何處理呢?你要知道按我們現(xiàn)在的方式,每個(gè)業(yè)務(wù)方法都處于各自獨(dú)立的事務(wù)上下文當(dāng)中(Transaction Context),互相調(diào)用形成了嵌套事務(wù),此時(shí)你又該如何處理?也許辦法就是重新寫一遍,把不同的業(yè)務(wù)方法集中成一個(gè)巨無霸包裝在一個(gè)事務(wù)上下文中。 

        我們有更為優(yōu)雅的設(shè)計(jì)來解決這類問題,如果我們把Transaction Context的控制交給一個(gè)被業(yè)務(wù)代表對象、DAO和其他Component所共知的外部對象。當(dāng)業(yè)務(wù)代表對象的某個(gè)方法需要事務(wù)管理時(shí),它提示此外部對象它希望開始一個(gè)事務(wù),外部對象獲取一個(gè)連接并且開始數(shù)據(jù)庫事務(wù)。也就是將事務(wù)控制從service層抽離,當(dāng)web層調(diào)用service層的某個(gè)業(yè)務(wù)代表對象時(shí),返回的是一個(gè)經(jīng)過Transaction Context外部對象包裝(或者說代理)的業(yè)務(wù)對象。此代理對象將請求發(fā)送給原始業(yè)務(wù)代表對象,但是對其中的業(yè)務(wù)方法進(jìn)行事務(wù)控制。那么,我們?nèi)绾螌?shí)現(xiàn)此效果呢?答案是JDK1.3引進(jìn)的動(dòng)態(tài)代理技術(shù)。動(dòng)態(tài)代理技術(shù)只能代理接口,這也是為什么我們需要業(yè)務(wù)接口BookStoreManager的原因。
        首先,我們引入這個(gè)Transaction Context外部對象,它的代碼其實(shí)很簡單,如果不了解動(dòng)態(tài)代理技術(shù)的請先閱讀其他資料。 








      




    import java.lang.reflect.InvocationHandler; 
    import java.lang.reflect.Method; 
    import java.lang.reflect.Proxy; 

    import java.sql.Connection; 

    import com.strutslet.demo.service.SystemException; 

    public final class TransactionWrapper { 

        /** 
         * 裝飾原始的業(yè)務(wù)代表對象,返回一個(gè)與業(yè)務(wù)代表對象有相同接口的代理對象 
         */ 
        public static Object decorate(Object delegate) { 
            return Proxy.newProxyInstance(delegate.getClass().getClassLoader(), 
                    delegate.getClass().getInterfaces(), new XAWrapperHandler( 
                            delegate)); 
        } 
        
        //動(dòng)態(tài)代理技術(shù) 
        static final class XAWrapperHandler implements InvocationHandler { 
            private final Object delegate; 

            XAWrapperHandler(Object delegate) { 
               this.delegate = delegate; 
            } 
            
            //簡單起見,包裝業(yè)務(wù)代表對象所有的業(yè)務(wù)方法 
            public Object invoke(Object proxy, Method method, Object[] args) 
                    throws Throwable { 
                Object result = null; 
                Connection con = ConnectionManager.getConnection(); 
                try { 
                    //開始一個(gè)事務(wù) 
                    con.setAutoCommit(false); 
                    //調(diào)用原始業(yè)務(wù)對象的業(yè)務(wù)方法 
                    result = method.invoke(delegate, args); 
                    con.commit();   //提交事務(wù) 
                    con.setAutoCommit(true); 
                } catch (Throwable t) { 
                    //回滾 
                    con.rollback(); 
                    con.setAutoCommit(true); 
                    throw new SystemException(t); 
                } 

                return result; 
            } 
        } 



        正如我們所見,此對象只不過把業(yè)務(wù)對象需要事務(wù)控制的業(yè)務(wù)方法中的事務(wù)控制部分抽取出來而已。請注意,業(yè)務(wù)代表對象內(nèi)部調(diào)用自身的方法將不會開始新的事務(wù),因?yàn)檫@些調(diào)用不會傳給代理對象。如此,我們?nèi)コ舜碇貜?fù)的味道。此時(shí),我們的業(yè)務(wù)代表對象修改成:


    public class BookStoreManagerImpl implements BookStoreManager { 
        public boolean buyBook(String bookId)throws SystemException{ 
              Connection conn=ConnectionManager.getConnection();// 獲取數(shù)據(jù)庫連接 
              boolean b=false; 
              try{ 
                  BookDAO bookDAO=DAOFactory.getBookDAO(); 
                  CustomerDAO customerDAO=DAOFactory.getCustomerDAO(); 
                  // 嘗試從庫存中取書 
                  if(BookDAO.reduceInventory(conn,bookId,quantity)){ 
                      BigDecimal price=BookDAO.getPrice(bookId);  // 取價(jià)格 
                      // 從客戶帳戶中扣除price*quantity的費(fèi)用 
                      b= 
                      CustomerDAO.reduceAccount(conn,price.multiply(new BigDecimal(quantity)); 
                      .... 
                      其他業(yè)務(wù)方法,如通知管理員,生成定單等. 
                      ... 
                  } 
              }catch(SQLException e){ 
                 throws new SystemException(e); 
              } 
              return b; 
        } 
        .... 



        可以看到,此時(shí)的業(yè)務(wù)代表對象專注于實(shí)現(xiàn)業(yè)務(wù)邏輯,它不再關(guān)心事務(wù)控制細(xì)節(jié),把它們?nèi)课薪o了外部對象。業(yè)務(wù)代表工廠也修改一下,讓它返回兩種類型的業(yè)務(wù)代表對象:



    public final class ManagerFactory { 
          //返回一個(gè)被包裝的對象,有事務(wù)控制能力 
          public static BookStoreManager getBookStoreManagerTrans() { 
              return (BookStoreManager) TransactionWrapper 
                      .decorate(new BookStoreManagerImpl()); 
          } 
          //原始版本 
          public static BookStoreManager getBookStoreManager() { 
             return new BookStoreManagerImpl(); 
          } 
          ...... 
       } 


        我們在業(yè)務(wù)代表工廠上提供了兩種不同的對象生成方法:一個(gè)用于創(chuàng)建被包裝的對象,它會為每次方法調(diào)用創(chuàng)建一個(gè)新的事務(wù);另外一個(gè)用于創(chuàng)建未被包裝的版本,它用于加入到已有的事務(wù)(比如其他業(yè)務(wù)代表對象的業(yè)務(wù)方法),解決了嵌套業(yè)務(wù)代表對象的問題。
       我們的設(shè)計(jì)還不夠優(yōu)雅,比如我們默認(rèn)所有的業(yè)務(wù)代表對象的方法調(diào)用都將被包裝在一個(gè)Transaction Context。可事實(shí)是很多方法也許并不需要與數(shù)據(jù)庫打交道,如果我們能配置哪些方法需要事務(wù)聲明,哪些不需要事務(wù)管理就更完美了。解決辦法也很簡單,一個(gè)XML配置文件來配置這些,調(diào)用時(shí)判斷即可。說到這里,了解spring的大概都會意識到這不正是聲明式事務(wù)控制嗎?正是如此,事務(wù)控制就是AOP的一種服務(wù),spring的聲明式事務(wù)管理是通過AOP實(shí)現(xiàn)的。AOP的實(shí)現(xiàn)方式包括:動(dòng)態(tài)代理技術(shù),字節(jié)碼生成技術(shù)(如CGLIB庫),java代碼生成(早期EJB采用),修改類裝載器以及源代碼級別的代碼混合織入(aspectj)等。我們這里就是利用了動(dòng)態(tài)代理技術(shù),只能對接口代理;對類的動(dòng)態(tài)代理可以使用cglib庫。

    評論

    # re: 事務(wù)處理[未登錄]  回復(fù)  更多評論   

    2008-06-01 18:43 by robin
    用你最后介紹的方法試了,但是不能回滾,我想是因?yàn)橛袃蓚€(gè)connection的原因吧!
    主站蜘蛛池模板: 亚洲AV永久无码天堂影院| 香蕉视频在线观看免费| 老司机永久免费网站在线观看| 亚洲av午夜国产精品无码中文字 | 亚洲国产精品无码第一区二区三区 | 成年美女黄网站色大免费视频| 菠萝菠萝蜜在线免费视频| 亚洲VA成无码人在线观看天堂| 日韩欧美一区二区三区免费观看 | 亚洲无删减国产精品一区| 黄页网站在线看免费| 免费播放国产性色生活片| 亚洲最大的成网4438| 免费国产美女爽到喷出水来视频| 国产午夜精品久久久久免费视| 亚洲乱码av中文一区二区| 亚洲欧洲无码AV电影在线观看| 性xxxx视频播放免费| 免费人成网上在线观看| 亚洲欧洲国产视频| 亚洲精品一级无码中文字幕 | 亚洲AV午夜成人影院老师机影院| 中文字幕无码视频手机免费看| g0g0人体全免费高清大胆视频| 国产精品亚洲四区在线观看 | 亚洲午夜久久久久久尤物| 国产亚洲精品AA片在线观看不加载| 亚洲免费福利视频| 搡女人免费免费视频观看| 成人男女网18免费视频| 国产 亚洲 中文在线 字幕| 在线精品免费视频| 亚洲日本VA午夜在线电影| 亚洲精品在线观看视频| 美腿丝袜亚洲综合| 性做久久久久免费观看| 性短视频在线观看免费不卡流畅| 免费在线黄色电影| 国产美女视频免费观看的网站 | 男人的天堂av亚洲一区2区| 亚洲精品午夜久久久伊人|