沉睡森林@漂在北京

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

            BlogJava :: 首頁 :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理 ::
            152 隨筆 :: 4 文章 :: 114 評論 :: 0 Trackbacks


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

          1、  問題描述

          涉及Web的應用,通常針對每次請求,Servlet容器產(chǎn)生一個新的線程。常用的DAO模式,對于每次dao操作都會獲得一個數(shù)據(jù)庫連接,操作完成后銷毀數(shù)據(jù)庫連接資源。一個最原始的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        每次調(diào)用都會初始化數(shù)據(jù)庫連接資源,使用完畢后清除資源。對于數(shù)據(jù)庫資源的創(chuàng)建和銷毀都會消耗大量的性能。

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

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

          總結上面的問題,可以發(fā)現(xiàn),其實這種常用的DAO模式根本無法滿足企業(yè)系統(tǒng)開發(fā)的需要,無法保證系統(tǒng)的性能和數(shù)據(jù)的完整性。那么,怎么樣的DAO設計才可以滿足企業(yè)開發(fā)的需要?

          2、  性能問題的解決

          對于上面的示例,性能的優(yōu)化主要在于數(shù)據(jù)庫資源的重用??紤]數(shù)據(jù)庫連接這種重量級的資源的寶貴性,我們可以建立一個緩存池,對連接進行池化操作。Pool的概念在JAVA EE的設計中經(jīng)常遇到,連接池的使用可以避免大量不需要的創(chuàng)建和銷毀產(chǎn)生的性能壓力。

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

          3、  事物問題的控制

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

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

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

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

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

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

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

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

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

           

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

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


          網(wǎng)站導航:
           
          主站蜘蛛池模板: 德令哈市| 紫金县| 双流县| 广水市| 东台市| 宜阳县| 神池县| 博客| 吴桥县| 扎赉特旗| 文水县| 察雅县| 东乡县| 大冶市| 濮阳市| 张北县| 从江县| 岳阳县| 池州市| 肃南| 连云港市| 格尔木市| 武穴市| 卢龙县| 临清市| 临猗县| 新乡县| 绥德县| 赤峰市| 淮北市| 聂荣县| 大化| 锦州市| 云霄县| 盱眙县| 阿巴嘎旗| 兰西县| 廊坊市| 韩城市| 山丹县| 阿图什市|