沉睡森林@漂在北京

          本處文章除注明“轉載”外均為原創,轉載請注明出處。

            BlogJava :: 首頁 :: 新隨筆 :: 聯系 :: 聚合  :: 管理 ::
            152 隨筆 :: 4 文章 :: 114 評論 :: 0 Trackbacks
          <2009年3月>
          22232425262728
          1234567
          891011121314
          15161718192021
          22232425262728
          2930311234

          隨筆分類(115)

          隨筆檔案(154)

          技術blog

          搜索

          最新評論


          最近看了幾篇關于數據庫事物的文章,很受啟發。對于復雜的業務系統,通常需要操作各種不同的數據源,為了保證數據的統一性和完整性,必須解決數據庫的事物問題。對于簡單的單數據源的事物,利用JDBC的事物控制就可以完成,跨數據源多庫的事物需要利用JTAAPI完成。

          1、  問題描述

          涉及Web的應用,通常針對每次請求,Servlet容器產生一個新的線程。常用的DAO模式,對于每次dao操作都會獲得一個數據庫連接,操作完成后銷毀數據庫連接資源。一個最原始的DAO模式代碼如下:

          publicvoid delete(Long id) throws AccountException {

                     Connection conn =null;

                     Statement stmt = null;

                     ResultSet rs = null;

                     try {

                           conn =DbUtil.openConn();

                           stmt = conn.createStatement();

                           stmt.executeQuery("DELETE  FROM TABLE_NAME");

          //              while(rs.next()){

                                //...

          //              }

                          

                     } catch (Exception e) {

                           e.printStackTrace();

                            //...

                     }finally{

                           DbUtil.close(conn, stmt, rs);

                     }

          }

               

          這種模式的問題很多,主要有以下幾點:

          l        每次調用都會初始化數據庫連接資源,使用完畢后清除資源。對于數據庫資源的創建和銷毀都會消耗大量的性能。

          l        使用了JDBC默認的數據庫事物提交方式,調用即提交數據庫事物。如果在該DAO函數里面完成多個操作的話,無法保證事務性。

          l        WEB層或者SERVICE層調用DAO方法時,如果一次需要調用多個DAO,那么會多次初始化和銷毀數據庫資源,事物也無法保證。

          總結上面的問題,可以發現,其實這種常用的DAO模式根本無法滿足企業系統開發的需要,無法保證系統的性能和數據的完整性。那么,怎么樣的DAO設計才可以滿足企業開發的需要?

          2、  性能問題的解決

          對于上面的示例,性能的優化主要在于數據庫資源的重用。考慮數據庫連接這種重量級的資源的寶貴性,我們可以建立一個緩存池,對連接進行池化操作。Pool的概念在JAVA EE的設計中經常遇到,連接池的使用可以避免大量不需要的創建和銷毀產生的性能壓力。

          基于上面的分析,我們可以設計一個連接池,設置最大連接數量,在系統啟動時初始化連接池。當然,我們可以分別設計一個使用中的連接池和一個空閑的連接池,存放不同狀態的連接。當一個連接被請求獲取,那么它進入InUse狀態,當請求操作完成,該連接進入Idlesse狀態。當然,Idlesse狀態的連接被請求獲取后會進入InUse狀態。

          3、  事物問題的控制

          一般事物控制有兩種情況,一種是聲明式的事物,如EJBSpringAOP等;另外一種是采用直接編碼控制事物。聲明式的事物一般是采用AOP設置代理類,最終和編碼的方式在本質上是一致的。這里我們主要討論編碼式的事物控制。在實際的開發中,可能會跨數據庫處理事物,需要利用JTA控制,這里先考慮簡單的單個數據源的事物控制,主要利用JDBC實現。

          對于Web應用的事物控制,核心的問題是事物應該被確定在那個層面上進行。一個最常見的解決方案是統一在Service層處理,比如Spring整合Hibernate時利用AOPService層控制事物。事物控制在Service層遇到的問題是,當Controller層需要處理一個復雜的業務時,可能會調用ServiceAServiceB,這時,兩個Service的事物沒有辦法保證一致。簡單的解決辦法是多寫一個ServiceC類,在ServiceC中完成復雜的業務功能。并且,在ServiceC中,是不能調用ServiceA或者ServiceB的,否則會產生事物的嵌套。當業務越來越復雜,代碼的利用率會降低。有人提出的解決方案是將Service分類,分為ABC三大類。

          A類為簡單的操作類Service,即不需要調用DAO函數,不需要操作數據庫,這類Service可以在名稱后面加上后綴區分,如AccountHelper。這類Service就是簡單的輔助對象,可以在任何地方調用。

          B類為需要操作DAO函數的,我們直接命名為AccountTransaction。這類Service直接提供給Controller調用。這類Service需要復雜打開和關閉Conn,即簡單的理解為conn.begin()conn.commit()就位于這類Service的開始和結束,其他的Service對象不能嵌套的調用他們。

          C類為需要調用DAO函數的,但是他們不能直接被Controller調用,他們不負責事物的begincommit。他們只能被B類調用,由B類負責事物控制。

          Service被分解為三大類后,你可以使用SpringAOP功能配置他們的事物控制代理了。這種方案可以很好的解決事物問題。但是,這種方案的可操作性還是有問題,面對換了一撥又一撥的開發人員,代碼的規范和質量不能很好的保證,那么只能實現一種更加簡單可行的方案了。

          當一個Web請求被響應,Servlet會產生一個新的線程,我們可以利用這種唯一的ThreadId作為唯一鍵標記一個Connection,所有基于該請求的操作都利用該Conn完成,事物的控制全部交給該Conn。即我們將事物控制從Service層提到Controller層。我們對Struts的配置文件做一點變通,對于每個Action加入一個關于事物控制的屬性。當不涉及數據庫操作的請求,我們設置為事物無關的,設計數據庫操作的請求設為事物控制的,稍稍減少一下無數據庫操作對事物控制的浪費。考慮下一個需要數據庫操作的Action被請求后,后臺是如何運作的。

          首先,取得當前的ThreadId,在連接池中找到一個空閑的Conn,然后把它放置到InUsed池內,同時begin一個事物。Service類不需要什么特殊的修改,不管多少次的嵌套,不管操作多少次的DAO,當需要數據庫操作時,我們利用當前的ThreadIdInUsed池內取得Conn。經歷了N多操作后,在Action負責轉向之前,我們利用當前的ThreadId得到了Conn,我們進行commit。或者在任何一個Exception,我們rollback。當然有一點小小的補充,當類似批處理的請求被提交,我們需要完成大量的工作,可能需要分批次的提交事物。比如,我們要做一個百萬數據導入另外一個表中,我們不能因為9999999條失敗了而回滾了整個事物。我們可以提供一些額外的API,完成個性化的事物控制,將大事物分解為小的事物,分批提交。

          上面的文字,完成了對一個簡單的JDBC事物控制的解決方案。對于復雜的業務系統,犧牲部分性能,將事物交給Controller層處理,會比Service層處理得到更加好的開發效率和事物保證。且不論開發人員的素質問題,在利用SpringAOP功能時,同樣會NAOP代理的性能消耗。而Controller控制事物,開發人員可以完全不予理會事物操作,不會有嵌套事物,不需要命名規則。

           

          posted on 2009-03-07 13:34 王總兵 閱讀(1369) 評論(0)  編輯  收藏

          只有注冊用戶登錄后才能發表評論。


          網站導航:
           
          主站蜘蛛池模板: 佳木斯市| 噶尔县| 江北区| 包头市| 静宁县| 获嘉县| 加查县| 永修县| 大足县| 马尔康县| 龙胜| 乌什县| 屏东市| 和平区| 昭通市| 长寿区| 阳新县| 临猗县| 葵青区| 蒙自县| 平江县| 阿鲁科尔沁旗| 大邑县| 建阳市| 南和县| 兰考县| 苏尼特左旗| 安康市| 高安市| 灵台县| 喀喇沁旗| 辰溪县| 岚皋县| 临西县| 宁安市| 封丘县| 和平县| 新乡县| 阳东县| 齐河县| 泰安市|