學(xué)會(huì )選擇何種設計模式和構架才可以開(kāi)發(fā)出最好的企業(yè)程序
摘要文章中,列舉了Chris Richardson在POJOs in Action(2006年1月份出版)的例子,該例子舉了5個(gè)程序設計者在設計企業(yè)應用程序中都會(huì )問(wèn)自己的問(wèn)題.
如果我們盲目的使用POJOs技術(shù)(plain-old Java objects)和輕量級構架,那么我們在通過(guò)EJB建立分布式企業(yè)級JAVA程序時(shí)就可能會(huì )出現錯誤。每種技術(shù)都有它的強項和弱項,而根據實(shí)際情況選擇最合適的技術(shù)是最重要的。
這篇文章主要討論企業(yè)應用程序的設計模式和輕量級構架。為了讓你在程序中高效的使用這些設計模式和輕量級構架,這里提供了一個(gè)決策構架. 這個(gè)構架包含了5個(gè)在設計程序或者是單獨用例的業(yè)務(wù)邏輯(business logic for an individual use-case)的過(guò)程中必須回答的問(wèn)題(decision-making framework)。這里特意加上了設計方案和對這種決策的理解,這樣可以很大的提高你的軟件質(zhì)量。
在這篇文章中,你會(huì )看到對5種決策的簡(jiǎn)述,并且我會(huì )簡(jiǎn)單扼要的介紹每種設計決策的選項(options)和他們的優(yōu)缺點(diǎn)。
版權聲明:任何獲得Matrix授權的網(wǎng)站,轉載時(shí)請務(wù)必保留以下作者信息和鏈接作者:Chris Richardson;
tain198127(作者的blog:
http://blog.matrix.org.cn/page/tain198127)
原文:
http://www.javaworld.com/javaworld/jw-01-2006/jw-0130-pojo.html譯文:
http://www.matrix.org.cn/resource/article/44/44254_J2EE+design+decisions.html關(guān)鍵字:J2EE;design;decisions
業(yè)務(wù)邏輯和數據庫訪(fǎng)問(wèn)決策這里有2種完全不同的方法來(lái)設計JAVA企業(yè)程序,其中一種選擇是采用標準EJB2實(shí)現途徑(approach)。我更愿意稱(chēng)這種方法為重量級實(shí)現途徑,當你使用重量級實(shí)現途徑時(shí)你需要用會(huì )話(huà)beans(session bean)和消息驅動(dòng) beans(message-driven bean)去實(shí)現業(yè)務(wù)邏輯。你也可以使用DAOs(data access object)或者實(shí)體bean去訪(fǎng)問(wèn)業(yè)務(wù)邏輯
另外一種選擇是使用POJOs 和輕量級構架,這種方式我稱(chēng)為POJO實(shí)現途徑。當使用POJOs實(shí)現途徑時(shí),你的業(yè)務(wù)邏輯完全由POJO來(lái)實(shí)現。你可以使用持久型構架又叫做對象/關(guān)系映射構架(a.k.a=also know as )例如Hibernate 或者 JDO來(lái)訪(fǎng)問(wèn)數據庫,再用Spring AOP(面向層面編程)來(lái)提供企業(yè)服務(wù),比如事務(wù)管理和安全。
EJB3由于融合了POJOs和其他一些輕量級概念,所以對兩者(指輕量級和重考鍛揪叮┑那植皇嗆芮宄?。举个例讬熏POJO中的實(shí)體bean既可以再EJB容器內運行,也可以再EJB容器外運行,然而POJOs中的會(huì )話(huà)bean和消息驅動(dòng)bean仍然有重量級的行為,因為他們只能在EJB容器內部運行。所以,顯而易見(jiàn)的,EJB3既是重量級的又有POJO的特性。EJB3中的實(shí)體bean是輕量級實(shí)現途徑中的一部分。
在開(kāi)發(fā)過(guò)程中,首要的是從各種各樣的設計中選擇到底采用重量級實(shí)現途徑還是采用POJO實(shí)現途徑。決策可以影響程序的幾個(gè)方面,包括業(yè)務(wù)邏輯結構和數據訪(fǎng)問(wèn)機制。為了幫助從兩種實(shí)現途徑中擇其一,來(lái)看這張典型的企業(yè)應用程序結構圖,結構圖在圖示1中,而且在設計過(guò)程中就必須判斷到底使用那種策略。
Figure 1. A typical application architecture and the key business logic and database access design decisions.
程序由網(wǎng)絡(luò )基本表示層、業(yè)務(wù)層、持久層組成。網(wǎng)絡(luò )基本表示層負責HTTP請求和為一般的瀏覽器客戶(hù)端、XML和其他的胖體客戶(hù)端生成HTML,比如為Ajax基本客戶(hù)端生成HTML。業(yè)務(wù)層被表示層調用,用來(lái)實(shí)現程序業(yè)務(wù)邏輯。持久層被業(yè)務(wù)邏輯層用來(lái)訪(fǎng)問(wèn)外部數據源,比如數據庫和其他程序。
表示層的設計不在本篇文章討論之內,來(lái)看圖表的其他部分,我們需要決定業(yè)務(wù)層結構的接口,這個(gè)接口是提供給表示層以及其他客戶(hù)端的。而且還需要決定怎樣訪(fǎng)問(wèn)能供多個(gè)程序訪(fǎng)問(wèn)的數據庫。我們還必須決定如何處理短期事務(wù)處理事務(wù)和長(cháng)期事務(wù)處理事務(wù)的并發(fā)問(wèn)題。這些加起來(lái)一共有5種決策。每種決策都是要設計者來(lái)制定,為了能看懂演示圖(big picture)要求每個(gè)開(kāi)發(fā)者也都了解這些策略。
這些決策直接決定程序業(yè)務(wù)和表示層設計的特點(diǎn)。當然,還要決定一些其他很重要的決策。比如業(yè)務(wù)處理(transactions)、安全問(wèn)題、緩存問(wèn)題以及如何整合程序,但是關(guān)于這些問(wèn)題通常在其他文獻中討論
在圖表1中顯示的五種決策,每種決策都有多種選擇。每種選擇根據它要解決的實(shí)際問(wèn)題都有相應的優(yōu)缺點(diǎn)。后續章節中,你會(huì )發(fā)現每種決策針對一個(gè)或多個(gè)領(lǐng)域時(shí),在功能性、易開(kāi)發(fā)性、可維護性和可用性方面有不同的平衡點(diǎn)。盡管我是POJO實(shí)現途徑的超級大FANS,但是仍然需要了解其優(yōu)缺點(diǎn),以便于為你的程序做最好的選擇
下面我們來(lái)了解一下每種決策的大綱和其選項。
決策1:組織業(yè)務(wù)邏輯現在,很多的注意力都集中在某項技術(shù)的優(yōu)點(diǎn)和缺點(diǎn),盡管這很重要,但是在本質(zhì)上你需要了解如何建構你的業(yè)務(wù)邏輯。如果不考慮如何組織就去寫(xiě)代碼是非常簡(jiǎn)單的。例如,為一個(gè)會(huì )話(huà)BEAN添加代碼要比在域模式(domain model.: An object model of the domain that incorporates both behavior and data.)中判斷應該添加那種新特性要簡(jiǎn)單的多。理論上你仍然需要刻意的為你的軟件設計最合適的業(yè)務(wù)邏輯。畢竟我相信你有過(guò)修改別人垃圾結構代碼的慘痛經(jīng)驗
關(guān)鍵的決策是:到底應該用面向對象的實(shí)現途徑還是面向過(guò)程的實(shí)現途徑來(lái)實(shí)現你的程序。這個(gè)不是關(guān)于技術(shù)的決策,但是你技術(shù)上的決策可以潛在的約束你的業(yè)務(wù)邏輯的組織結構。采用EJB2技術(shù),有利于面向過(guò)程設計,然而POJOs和輕量級構架可以讓你為特殊的程序選擇最好的實(shí)現途徑
采用過(guò)程式設計雖然我是一個(gè)面向對象實(shí)現途徑(指前文的使用POJO和LIGHTFRAMEWORK)的倡導者,但是有些情況下面向對象實(shí)現途徑有些大材小用,比如你只想實(shí)現一個(gè)非常簡(jiǎn)單的業(yè)務(wù)邏輯。而且,有時(shí)候,面向對象實(shí)現途徑不太可行-—比如,你沒(méi)有持久層構架來(lái)將你的對象映射到數據庫中,在這種情況下,更好的方法是編寫(xiě)面向過(guò)程的代碼,而且采用Martin Fowler稱(chēng)作事務(wù)腳本(Transaction Script)的設計模式,要比采用面向對象實(shí)現途徑設計要好,因為你只需要寫(xiě)一個(gè)方法來(lái)調用事務(wù)處理腳本去處理表示層的請求。
采種這種實(shí)現途徑的一個(gè)很重要的特點(diǎn)是,用于實(shí)現某種行為的類(lèi)和數據存儲區是分開(kāi)的。在EJB2的應用程序中,這種方式的業(yè)務(wù)邏輯和圖表2中的設計是非常相似的。這種設計的核心全都集中在EJB或者POJO的行為上,因為他們實(shí)現了事務(wù)腳本,并且還操作那些 “啞”對象數據(因為他們只擁有很少的行為,大部分都是數據)。因為大部分的行為都集中在少量的大型類(lèi)上,所以代碼會(huì )變的很難理解與維護。
Figure 2. The structure of a procedural design: large transaction script classes and many small data objects
這種設計具有高面向過(guò)程的特性,而且基本不依靠面向對象語(yǔ)言的特性。如果你曾經(jīng)使用過(guò)C或者其他非面向對象語(yǔ)言的話(huà),你應該用過(guò)這種設計模式。如果這種模式很適合你的設計的話(huà),用這種模式設計也是一種不錯的選擇。
這種直觀(guān)的過(guò)程式開(kāi)發(fā)途徑,非常的誘人,因為你只需要寫(xiě)代碼就好了,不用考慮如何組織你的類(lèi)文件。但問(wèn)題是,如果你的業(yè)務(wù)邏輯非常的復雜,那么你的代碼會(huì )變的噩夢(mèng)般的難以維護。所以,除非你要寫(xiě)的程序非常的簡(jiǎn)單,否則你應該用面向對象設計你的程序,而不要受面向過(guò)程的代碼的誘惑。
采用面向對象設計在面向對象設計中,業(yè)務(wù)邏輯是由對象模型構成的,對象模型是由許多小類(lèi)組成的關(guān)系網(wǎng)。這些類(lèi)直接體現的是問(wèn)題域的解決方法,如圖3所示,在這種模式中,有些類(lèi)只有數據,有些類(lèi)只有行為,但是大多數的則兩者都有,這是優(yōu)秀的類(lèi)設計的一種特點(diǎn)。
Figure 3. The structure of a domain model: small classes that have state and behavior
面向對象設計有許多的好處,包括可以提高可維護性和可延展性。你可以用EJB2的實(shí)體bean來(lái)實(shí)現一個(gè)簡(jiǎn)單的對象模型。但是如果像要獲得更多的好處的話(huà),必須要使用POJOs技術(shù)和輕量級持久層構架——比如Hibernate和JDO技術(shù)。POJO可以讓你開(kāi)發(fā)豐富的模型,這些模型可以擁有繼承和回調等特點(diǎn)。而輕量級持久層構架可以讓你很簡(jiǎn)單的從對象模型映射到數據庫。
對象模型的另外一個(gè)名字是域模型,Fowler稱(chēng)這種由面向對象途徑來(lái)開(kāi)發(fā)的業(yè)務(wù)邏輯叫做域模型設計模式。(就是類(lèi)的設計是直接用來(lái)解決問(wèn)題的,則這種設計模式叫做域模型設計模式)
表模型設計模式我曾經(jīng)一直用域模型和事務(wù)處理腳本模型設計應用程序。但是有一次我聽(tīng)說(shuō)JAVA企業(yè)應用程序可以用第三種途徑來(lái)實(shí)現,這種途徑就是Fowler所說(shuō)的表模型設計模式。這種模式比事務(wù)處理腳本模式更加的結構化,因為它為數據庫中的每個(gè)表都寫(xiě)了一個(gè)類(lèi),而這個(gè)類(lèi)中實(shí)現了所有對這個(gè)表的操作代碼,這個(gè)類(lèi)就是表模型類(lèi)。(我的解釋就是為每個(gè)表專(zhuān)門(mén)寫(xiě)個(gè)類(lèi),對表的所有操作,全都由這個(gè)類(lèi)中的方法實(shí)現,相當于用一個(gè)類(lèi)模擬的數據庫中的表)。和事務(wù)處理腳本模式相比,它將數據和行為分別封裝到了不同的類(lèi)中,因為表模型類(lèi)的實(shí)例相當于真實(shí)數據庫中的數據,這當然要比單獨的一條記錄要好的多。最后,可維護性成了問(wèn)題,然而表模型設計模式還是有一些好處的。
決策2:封裝業(yè)務(wù)邏輯前面幾章,我沒(méi)有提及如何組織業(yè)務(wù)邏輯。你必須決定業(yè)務(wù)邏輯有什么樣的接口。業(yè)務(wù)邏輯的接口由一些數據和方法組成,這些數據和方法由表示層來(lái)調用。在設計接口時(shí)重點(diǎn)需要考慮的是:應該封裝哪些業(yè)務(wù)邏輯的操作,而哪些操作不應該顯示給表示層。封裝接口可以提高程序的可維護性,因為通過(guò)隱藏業(yè)務(wù)邏輯的操作細節,可以實(shí)現修改業(yè)務(wù)邏輯而不影響表示層。缺點(diǎn)是,你必須為封裝業(yè)務(wù)邏輯而特意的寫(xiě)很多的代碼。
你還需要考慮其他重要的問(wèn)題,比如如何處理事務(wù)處理,安全,和遠程調用問(wèn)題。通常這些也是業(yè)務(wù)邏輯接口要負責的問(wèn)題。為了保證數據的連貫性,業(yè)務(wù)層的接口必須保證每個(gè)事務(wù)處理中的調用都能執行。同樣,也要驗證調用者是否有權限調用業(yè)務(wù)方法。業(yè)務(wù)層接口還要負責處理一些遠程客戶(hù)端的問(wèn)題。
來(lái)考慮一下選項。
EJB session façade經(jīng)典的J2EE解決方案是:用EJB來(lái)封裝業(yè)務(wù)邏輯-基本的session facade。EJB容器提供事務(wù)處理管理,安全,分布式事務(wù)處理和遠程訪(fǎng)問(wèn)。Facade方式可以通過(guò)封裝業(yè)務(wù)邏輯來(lái)提高程序可維護性。粗糙型(Coarse-grained) API通過(guò)減少表示層對業(yè)務(wù)層的訪(fǎng)問(wèn)次數,而提高性能(因為它將對各個(gè)業(yè)務(wù)流程的處理再封裝了一次,所以對底層的業(yè)務(wù)流程來(lái)說(shuō),它的API是比較粗糙的,這里也許翻譯的不好。請大家見(jiàn)諒)。因為減少調用的次數,可以減少對數據庫事務(wù)處理的次數,還可以提高對象在緩沖區的機會(huì )。如果表示層通過(guò)遠程訪(fǎng)問(wèn)業(yè)務(wù)層,則這種API還可以減少網(wǎng)絡(luò )負擔。圖表4給出了一個(gè)EJB-based session facade的例子。
Figure 4. Encapsulating the business logic with an EJB session façade
在這種設計模式中,表示層也許是通過(guò)遠程來(lái)調用facade(相當于session的一個(gè)高級接口),EJB容器從facade中得到這個(gè)調用,并驗證調用者的權限,然后開(kāi)始一個(gè)業(yè)務(wù)處理。這個(gè)時(shí)候facade調用底層的業(yè)務(wù)對象,而這些業(yè)務(wù)對象負責實(shí)現具體的業(yè)務(wù)邏輯。等Facade返回后,EJB容器提交業(yè)務(wù)處理或者讓該業(yè)務(wù)處理循環(huán)等待。
不幸的是,使用EJB session facade有一些嚴重的缺點(diǎn)。比如,EJB的會(huì )話(huà)bean只能在EJB容器中運行,這樣就托慢了開(kāi)發(fā)和測試周期。另外,如果用EJB2,則用來(lái)向表示層傳輸數據的數據傳輸對象的開(kāi)發(fā)和維護就會(huì )變的很枯燥而且曠日持久。
POJO facade對于許多程序來(lái)說(shuō),更好的實(shí)現途徑是用POJO facade和AOP協(xié)作。比如負責管理事務(wù)處理、表示層的連接和安全問(wèn)題的Spring 構架。POJO facade對業(yè)務(wù)層的封裝風(fēng)格和EJB facade很相似,通常也可以用一樣的公共方法。而POJO和EJB關(guān)鍵區別是用POJO代替了EJB,用AOP提供的服務(wù)(例如業(yè)務(wù)處理管理和安全機制)替代了EJB容器。表5中,顯示了用POJO facade的例子。
Figure 5. Encapsulating the business logic with a POJO façade
表示層調用POJO facade, POJO facade 調用業(yè)務(wù)對象。和EJB容器截獲EJB facade方式一樣,AOP通過(guò)“攔截機”來(lái)截獲POJO facade,并驗證調用者的權限,然后開(kāi)始提交業(yè)務(wù)處理或讓該業(yè)務(wù)循環(huán)等待。
通過(guò)在應用程序服務(wù)器外部開(kāi)發(fā)和調試業(yè)務(wù)邏輯,對POJO facade的開(kāi)發(fā)可以變的很簡(jiǎn)單,同時(shí)還可以獲得許多EJB中會(huì )話(huà)Bean的好處,比如聲明事務(wù)處理和安全。關(guān)鍵是,你可以少寫(xiě)點(diǎn)代碼。你可以避免寫(xiě)數據傳輸對象類(lèi),因為POJO facade可以將對象域直接反饋給表示層;你可以使用依賴(lài)注射的方式來(lái)將應用程序組裝起來(lái),而不用在為JNDI寫(xiě)查找代碼了。
然而,有些時(shí)候不能那用POJO facade,比如它不能參與到遠程客戶(hù)端建立的分布式事務(wù)處理。
暴露模型域模式使用facade的一個(gè)缺點(diǎn)是你必須寫(xiě)額外的代碼,而且負責將對象域返回給表示層的代碼很容易出錯。如果表示層設法調用某個(gè)對象,而業(yè)務(wù)層卻沒(méi)有提供該對象,也會(huì )增加runtime error出現的機會(huì )。如果你用JDO , Hibernate或者EJB3,則可以避免這種問(wèn)題,方法是:將模型域(session區域)暴露給表示層,再將相應的對象域(存儲對象的區域)返回給表示層,根據表示層在對象域之間的操作關(guān)系,持久層來(lái)導入相應的對象。(也就是把session區域給表示層,然后分析它需要的對象,再讓持久層去加載這些對象)這就是所謂的lazy loading 技術(shù)。圖表6中顯示了表示層自由的訪(fǎng)問(wèn)對象域的設計圖。
Figure 6. Using an exposed domain model
在圖表6的設計中,表示層不通過(guò)facade而直接調用域對象,Spring AOP仍然提供服務(wù),例如事務(wù)處理管理和安全。
用這種實(shí)現途徑的一個(gè)重要的好處是,業(yè)務(wù)層不需要知道哪些對象需要調用,也不用知道那些需要返回給表示層。盡管這挺起來(lái)很簡(jiǎn)單,但是你會(huì )發(fā)現一些缺點(diǎn)。這會(huì )增加表示層的復雜度,因為你必須處理對數據庫的連接。而且在基于Web的應用程序中,事務(wù)處理管理也要非常小心,因為在表示層將數據反饋給瀏覽器之前,事務(wù)處理的數據必須保持正確。
決策3:訪(fǎng)問(wèn)數據庫無(wú)論你怎樣對業(yè)務(wù)邏輯怎樣的組織和封裝,最終你還是要從數據庫中取數據出來(lái)。在經(jīng)典的J2EE應用程序中,你有2個(gè)選擇:JDBC——這個(gè)需要很多的底層代碼;或者實(shí)體Bean——這個(gè)用起來(lái)非常困難,而且缺少重要特征。相比來(lái)說(shuō),使用輕量級構架令人高興的事情之一就是:你有一些新的而且更有力的方法去訪(fǎng)問(wèn)數據庫,而且這種方法可以顯著(zhù)的減少訪(fǎng)問(wèn)數據庫的代碼。讓咱們來(lái)進(jìn)一步研究
直接用JDBC會(huì )有什么問(wèn)題最近突然出現了對象/關(guān)系 映射構架(比如JDO和Hibernate) 和SQL映射構架(比如iBATIS)這些不是憑空出現的。相反,他們是在JAVA 聯(lián)盟在JDBC屢造挫折之后才出現的。為了了解新構架出現的原因,這里咱們回顧一下直接使用JDBC會(huì )出現的問(wèn)題。在許多程序中直接使用JDBC不是一個(gè)好的選擇,主要有以下三個(gè)原因:
· 開(kāi)發(fā)和維護SQL非常的困難而且耗費時(shí)間——一些開(kāi)發(fā)者發(fā)現要寫(xiě)龐大而且復雜的SQL語(yǔ)句非常的困難。反映數據庫變化的SQL語(yǔ)句會(huì )變得非常耗時(shí)。你必須小心的考慮犧牲可維護性是否值得。
· 用SQL會(huì )使移植性變的很差——因為需要數據庫的特殊SQL語(yǔ)句。如果一個(gè)程序和多個(gè)數據庫有關(guān)系,那么你就要寫(xiě)多個(gè)版本的SQL語(yǔ)句,這使得可維護性變變成噩夢(mèng)。
· 直接寫(xiě)JDBC代碼要會(huì )非常耗時(shí),而且容易出錯。你必須寫(xiě)很多的樣板代碼去獲得連接,創(chuàng )建和初始化適當的聲明,還要用精確的聲明去清理連接。而且你還要寫(xiě)代碼去將JAVA 對象映射到SQL聲明。由于要無(wú)奈的去寫(xiě),
JDBC代碼很容易出錯。
如果你的程序必須直接運行SQL語(yǔ)句的話(huà),那前面兩個(gè)問(wèn)題是無(wú)法避免的。有時(shí)候為了獲得好的性能,必須要全力的寫(xiě)SQL語(yǔ)句,包括供應商提供的那些特殊東西。由于許多業(yè)務(wù)上的原因,持久層可能會(huì )產(chǎn)生混亂的SQL語(yǔ)句,為了防止這種情況,DBA可能要求你的程序來(lái)完全控制SQL語(yǔ)句的執行。通常,團隊買(mǎi)進(jìn)的關(guān)系型數據庫過(guò)于龐大,以至于應用程序工作時(shí)會(huì )出現一些和數據庫有關(guān)的瑣碎事務(wù)。根據”iBATIS in Action”的作者說(shuō)這里會(huì )有一種情況出現:“數據庫或者SQL語(yǔ)句本身存在的時(shí)間比程序代碼存在的時(shí)間還要長(cháng),或者同一段SQL語(yǔ)句或數據庫有多個(gè)程序的版本。有些情況下,程序已經(jīng)用另外一種語(yǔ)言重寫(xiě)了,但是SQL語(yǔ)句和數據庫卻沒(méi)有太大的改變。” 如果直接使用SQL弄的你筋疲力盡,那么很幸運,這里有一種直接執行SQL語(yǔ)句的構架,它可比用JDBC要容易多了。當然了,這就是iBATIS.
使用 iBATIS我開(kāi)發(fā)過(guò)的所有企業(yè)JAVA應用程序,都是直接執行SQL語(yǔ)句的。早期的程序是執行特定的SQL語(yǔ)句的,后來(lái)是用持久層構架再用少量的SQL語(yǔ)句構成的。一開(kāi)始我直接用JDBC來(lái)執行SQL語(yǔ)句,但是后來(lái),我經(jīng)常寫(xiě)一些小的構架去完成JDBC中那些比較無(wú)聊的部分。我也用過(guò)一段Spring的JDBC類(lèi),這些類(lèi)除去了JDBC中的許多樣板代碼。但是無(wú)論是我自己寫(xiě)的構架還是使用Spring的類(lèi),在Java類(lèi)映射到SQL語(yǔ)句的時(shí)候都會(huì )存在問(wèn)題,這就是我為什么那么高興的加入iTATIS 那邊的原因了。
iBATIS 不僅將應用程序完全的與“數據庫連接”、具體的SQL語(yǔ)句隔絕開(kāi)來(lái),更實(shí)現了通過(guò)XML描述文檔來(lái)將JavaBean 映射到SQL語(yǔ)句。它用Java bean 內省機制來(lái)將“道具bean(bean properties)”映射為相應的數據庫語(yǔ)句占位符,而且它可以將ResultSet后的結果構造為bean。它還可以通過(guò)數據庫生成主鍵,自動(dòng)加載相關(guān)的對象、實(shí)現緩存和lazy loading。這樣,iBATIS 就除去了許多執行SQL語(yǔ)句帶來(lái)的苦差。通過(guò)編輯XML描述文檔和調用少量的iBATIS的API,代替了寫(xiě)大量的JDBC底層代碼。
使用持久層框架當然,iBATIS不能實(shí)現高層開(kāi)發(fā)和維護SQL語(yǔ)句,而且缺乏可移植性。為了避免這類(lèi)問(wèn)題,你需要用到持久層框架。持久層框架可以將對象域映射到數據庫中。它提供了創(chuàng )建,查找,刪除對象的API函數。當程序要控制對象時(shí)它可以自動(dòng)的加載相應的對象,還可以在事務(wù)處理結束時(shí)自動(dòng)更新數據庫。持久層框架通過(guò)對象/關(guān)系映射機制可以自動(dòng)的生成SQL語(yǔ)句,對象/關(guān)系映射機制用XML文檔定義了怎樣將類(lèi)映射為表,怎樣將數據映射為列(column)和關(guān)系是怎樣被映射為外鍵與連接表的。
在持久層構架上EJB也有它的短處:實(shí)體bean。EJB2的實(shí)體bean有很多的不足,而且開(kāi)發(fā)和測試它會(huì )變得非常的枯燥。最后,很少用EJB2的實(shí)體bean了。在EJB3中會(huì )說(shuō)明那些問(wèn)題。
兩種最有流行的輕量級持久層構架是JDO和Hibernate,前者是Sun的標準框架,后者是開(kāi)源工程。兩種框架都可以為POJO類(lèi)提供持久層事務(wù)處理。你可以用POJO類(lèi)來(lái)開(kāi)發(fā)和測試你的業(yè)務(wù)邏輯,而不用擔心持久層的問(wèn)題,這個(gè)時(shí)候它會(huì )將類(lèi)映射到數據庫中的schema。另外,他們兩個(gè)都可以在服務(wù)器程序外部或者內部,這樣可以進(jìn)一步降低開(kāi)發(fā)難度。用Hibernate和JDO來(lái)進(jìn)行開(kāi)發(fā)比用老的EJB2的實(shí)體bean要舒服的多。
除了要決定怎樣訪(fǎng)問(wèn)數據庫外,還要決定如何處理數據庫的并行處理問(wèn)題。下面來(lái)看一下,為什么并行處理問(wèn)題那么重要,同時(shí)看一下可實(shí)現的選項
決策4:處理數據庫事務(wù)處理的并行問(wèn)題差不多所有的企業(yè)應用程序都需要多用戶(hù)和多個(gè)后臺進(jìn)程并行的更新數據庫。2個(gè)數據庫 處理事務(wù)同時(shí)訪(fǎng)問(wèn)同時(shí)訪(fǎng)問(wèn)同一個(gè)數據是很正常的,但是這種情況很可能引起數據庫中的數據不一致或者引起應用程序的不正常。由于大部分的應用程序都需要處理多個(gè)處理事務(wù)并行訪(fǎng)問(wèn)同一個(gè)數據,則它可以影響到業(yè)務(wù)和持久層的設計。
無(wú)論你是使用EJB還是輕量級構架,你的程序必須可以并行訪(fǎng)問(wèn)共享數據。EJB2要求使用供應商提供的特殊擴充接口來(lái)實(shí)現并行,然而與此不同的是,JDO和Hibernate可以直接支持大部分并行機制。更重要的是,使用JDO和Hibernate不僅只配置簡(jiǎn)單,而且只需要少量的代碼就可以實(shí)現了。
在這樣主要介紹幾種“并行更新數據庫處理事務(wù)”的選項的概要,這些事務(wù)處理和用戶(hù)的輸入無(wú)關(guān)。下一章,我主要介紹一下如何在應用程序級長(cháng)時(shí)間的并行更新數據庫處理事務(wù),這種處理事務(wù)會(huì )與用戶(hù)輸入有關(guān),而且是由一系列的數據庫事務(wù)處理組成的。
獨立數據庫事務(wù)有時(shí)候對共享數據的并行訪(fǎng)問(wèn)可以簡(jiǎn)單的依靠數據庫本身來(lái)實(shí)現,數據庫可以設置為執行孤立的數據——這只是對數據庫而言。如果你對這種概念不熟悉也不要擔心,你只要記?。喝绻麘贸绦蚴褂猛耆墓铝⑹聞?wù)方式,那么同時(shí)執行2個(gè)事務(wù)的結果和一個(gè)接一個(gè)的執行是一樣的。(也就是說(shuō),如果你用孤立事務(wù)的方式來(lái)訪(fǎng)問(wèn)數據庫的話(huà),你同時(shí)執行2個(gè)事務(wù),就會(huì )變成一個(gè)接一個(gè)的串行執行了。)
這種方法也許聽(tīng)起來(lái)非常的簡(jiǎn)單,但問(wèn)題是這種處理方式有時(shí)候會(huì )降低性能,因為如何實(shí)現對事務(wù)的孤立是由數據庫來(lái)決定的。為了這個(gè)原因,許多應用程序都避免使用它,而采用optimistic或者pessimistic 所鎖,這會(huì )在下面講到。
開(kāi)放式鎖定并行更新數據的一種途徑是用開(kāi)放式鎖定。開(kāi)放式鎖定工作原理是通過(guò)應用程序來(lái)檢查數據是否被更新(被其他事務(wù)修改造成的)而實(shí)現的。一種更普通的實(shí)現開(kāi)放式鎖定的方法是在每個(gè)表中添加一個(gè)“版本列”(version column),對每個(gè)表而言,程序每次改變其中一行的時(shí)候都會(huì )更新這個(gè)“版本列”。每個(gè)UPDATE語(yǔ)句中的WHERE語(yǔ)句會(huì )根據上次查詢(xún)的結果判斷這個(gè)版本號是不是被更改了。在事務(wù)訪(fǎng)問(wèn)數據庫中的數據時(shí),程序中可以用PreparedStatement.executeUpadte()這個(gè)函數的返回值來(lái)檢查行的個(gè)數,從而判斷是否要繼續執行UPDATE語(yǔ)句。如果數據中的行已經(jīng)被其他的事務(wù)更新或者刪除了,那么程序會(huì )讓該事務(wù)從新訪(fǎng)問(wèn)數據庫。
用開(kāi)放式鎖定機制來(lái)鎖定那些直接執行SQL語(yǔ)句的應用程序是非常簡(jiǎn)單的。但是,用持久層構架(比如JDO和Hibernate)實(shí)現更容易,因為他們已經(jīng)提供了開(kāi)放式鎖定機制——在配置選項中。一旦在配置選項中,選中了這種方式,持久層構架會(huì )自動(dòng)的生成SQL的UPDATE語(yǔ)句來(lái)完成版本檢查的任務(wù)。開(kāi)放式鎖定的名字來(lái)源于一種假設的情況,在這種情況下:并發(fā)更新的機會(huì )非常少,而且程序只能檢測、覆蓋這些數據而不能防止這種事情的發(fā)生。另外一種可選的途徑是用保守式鎖定,使用他的假設條件是:并發(fā)更新肯定會(huì )發(fā)生,而且必須被禁止。
保守式鎖定對于開(kāi)放式鎖定來(lái)說(shuō),另外一種途徑是使用保守式鎖定。當一個(gè)事務(wù)讀取某些行的數據時(shí),他會(huì )對這些數據加鎖,這樣就防止其他的事務(wù)訪(fǎng)問(wèn)這些數據了。具體的實(shí)現是需要數據庫支持的,然而不幸的是,不是所有的數據庫都支持保守式鎖定。如果你的數據庫支持話(huà),那么你的應用程序直接執行SQL語(yǔ)句來(lái)實(shí)現保守式鎖定將非常容易。但是,可能你已經(jīng)猜到了,在程序中用JDO或者Hibernate來(lái)實(shí)現保守式鎖定更容易,JDO以配置選項的方式提供了保守式鎖定,而Hibernage提供了簡(jiǎn)單的API實(shí)現鎖定對象。
除了可以處理單個(gè)數據庫事務(wù)并行問(wèn)題,常常你還需要處理多數據庫事務(wù)的并行問(wèn)題。
決策5:在長(cháng)事務(wù)下處理并發(fā)訪(fǎng)問(wèn)獨立事務(wù)、開(kāi)放式鎖定、和保守式鎖定只能用在單個(gè)數據庫事務(wù)上的,然而,許多的程序需要 長(cháng)時(shí)間的 在多個(gè)數據庫事務(wù)之間 讀取或者更新 共享數據。比如,有一種情況描述的是 怎樣實(shí)現 用戶(hù)編輯命令,這和很多的進(jìn)程有關(guān),這些進(jìn)程可能會(huì )運行 很長(cháng)的時(shí)間,而且它由 多個(gè)數據庫事務(wù)組成。因為數據可能會(huì )被 一個(gè)數據庫事務(wù) 讀取,而又被 另外一個(gè)數據庫事務(wù) 修改了,那么程序必須對 共享數據的并發(fā)訪(fǎng)問(wèn) 進(jìn)行不同的處理。這樣就必須使用 開(kāi)放式鎖定設計模式或 者保守是鎖定設計模式,關(guān)于這兩種模式會(huì )在Fowler的 Patterns of Enterprise Application Architecutre中詳細介紹。
開(kāi)放式脫機鎖定模式一種選擇是開(kāi)放式鎖定機制的擴展,它會(huì )從第一次讀取數據開(kāi)始,在編輯進(jìn)程執行后檢查數據是否已經(jīng)被修改了。例如,你可以在數據庫的共享數據的表中使用版本號來(lái)實(shí)現。在編輯進(jìn)程開(kāi)始的時(shí)候,程序將版本號存儲到會(huì )話(huà)狀態(tài)中,然后每次用戶(hù)要存儲數據時(shí),應用程序都要進(jìn)行檢查,保證數據庫中的版本號和會(huì )話(huà)狀態(tài)中的版本號一致。
因為開(kāi)放式脫機鎖定模式只有在用戶(hù)進(jìn)行保存修改過(guò)的數據時(shí)才可以檢測,所以它只有在不成為客戶(hù)的累贅的時(shí)候,才可以很好的運行。但如果情況是:客戶(hù)必須要撤銷(xiāo)幾個(gè)操作的話(huà),那么就會(huì )因為這種鎖定模式而非??鄲?,那么更好的一種選擇是用保守式脫機鎖定。
保守式脫機鎖定模式在編輯進(jìn)程開(kāi)始時(shí),保守式脫機鎖定方式通過(guò)鎖定 共享數據,來(lái)解決 多個(gè)數據庫事務(wù) 同時(shí)更新共享數據的問(wèn)題,這樣,這個(gè)編輯進(jìn)程就可以防止 其他的用戶(hù)來(lái)修改數據了。這種方式和保守式鎖定機制一開(kāi)始描述的很類(lèi)似,但它是靠程序來(lái)實(shí)現的,而不是數據庫。因為同一時(shí)間內,只有有權利編輯共享數據的用戶(hù),才有權利去保存這些修改。