POJO式开发
POJO
?
??????POJO?就是簡單?java?對象,不實現任何特殊接口。?POJO?這一名字由?Fower?、?Rebbecca?、?Parsos?、?Josh MacKenzie(Foeler POJO)?發明,目的地是為了給普通?Java?對象取個令人興奮的、過目不忘的名字。
?
早期?EJB?及其存在的問題
?
??????EJB1.0?版本發布于?1998?年,它提供了兩種企業?bean?:會話?bean?和實體?bean?。會話?bean?便是無狀態服務或與客戶端之間的有狀態會話。實體?bean?表示數據庫里的數據,最初意在實現業務對象。?EHB2?提煉了?EJB?編程模型。不僅增加了支持由容器管理的關系增強型實體?bean?,還新增了消息驅動?bean(?負責處理?Java Message Service?或?JMS?,消息?)?。
?
EJB?存在的問題
?
???????盡管有很多書幫助開發人員對付?EJB?,并學會如何有效的使用?EJB?,但是?EJB?的;兩個主要問題并沒有直接解決。
?
第一,????EJB?鼓勵開發人員編寫過程式應用程序
第二,????使用?EJB?開發相當麻煩
?
過程式設計的缺點:
?
對業務邏輯的組織方式主要有兩種:過程式或面向對象。過程式方式以函數為單元組織代碼,這些函數操作單獨的簡單數據對象。在過程式架構中,數據結構遍布各處,并作為參數傳入函數,或返回給調用函數。?數據與操作之間的關系非常松散?,并且完全由開發人員自己維護。在面向對象語言出現之前,這種編程方式主導了軟件開發。
?
與之相比,面向對象方法則以對象為單元組織代碼,這些對象?具有狀態和行為?,并與其他對象協作。?數據結構和操作定義在一個語言構造單元內,數據和對數據操作并存于其中。數據和操作之間的關系?(?和狀態?)?由語言本省維護?。與過程式設計相比,面向對象設計更易理解、維護、擴展和測試。
?
如果業務邏輯夠簡單,過程式設計方法倒也不成問題,但是業務邏輯總有變得愈加復雜的趨勢。一旦需求改變,業務邏輯就必須實現新的特性,?EJB的代碼量會不斷增加。
?
EJB2?在一定程度上就是鼓勵人們編寫過程式代碼,?實現新行為時,不必像設計真正的對象模型那樣費心地識別類并賦予其職責。相反,你可以編寫一個新的會話?bean?方法或在現有方法里添加代碼?。
?
這鐘過程式的設計方法,有些開發人員仍把持久對象簡單的視為一種向數據庫存取數據和編寫過程式業務邏輯方法,這就是所謂的貧血模型
?
EJB?開發的麻煩:
?
n??????????你必須面對惱人而長的編輯?-?編譯?-?調試周期
n??????????你得面對關注點缺少分離的顯示
n????????????你必須編寫大量的代碼才能實現一個?EJB
n??????????你必須編寫數據傳輸對象?(DTO)
?
?
用?POJO?開發
?
???????用?POJO?進行開發,僅有?POJO?本身還是不夠的。在企業應用程序里,你還需要諸如事務管理、安全和持久化等服務,此前這些服務由?EJB?容器提供。現在的解決方案是使用所謂“輕量級”框架來代替?J2EE STACK?里的一些“重量級”部分。主要是?4?種輕量級框架:?Hibernate?、?JDO?、?Ibatis?和?Spring?。
?
???????這些技術的主要特征在于他們都是非侵入式的。它們提供事務和持久化時并不要求應用程序類實現任何特殊接口。甚至當應用程序的類需要運行在事務里或者持久化的時候,它們仍是?POJO?。
?
??????????????????????????????????????????典型的?EJB?和?POJO?方法比較
?
| ? | 典型的?EJB?方法 | POJO?方法 |
| 組織 | 過程式業務邏輯 | 面向對象設計 |
| 實現 | 基于?EJB | POJO |
| 數據庫訪問 | JDBC/SQL?或實體?Bean | 持久層框架 |
| 返回給表示層的數據 | DTO | 業務對象 |
| 事務管理 | EJB?容器管理的事務 | Spring?框架 |
| 應用程序組裝 | 顯示的?JNDI?查詢 | 依賴注入 |
?
?
l??????????面向對象設計
?
整個設計更容易理解和維護
更易于測試
更易擴展
?
l??????????使用?POJO
?
開發更加容易
更加快捷
可移植性增強
?
l??????????持久化?POJO
?
使用?JDO?和?Hibernate?提供透明持久化,這意味著類不會意識到它們是持久的。應用程序只需要調用持久層框架?API?保存、查詢和刪除持久對象、而且對測試也很方便。
?
l??????????消除?DTO
?
DTO?又稱為值對象(?value object?)。?DTO?只是一個由成員變量組成的簡單行為對象,用于從業務層向表示層返回數據。這是由于表示層無法高效地訪問?EJB2?實體?bean?,因此?EJB?程序需要?DTO?。
?
向表示層返回?Hibernate?、?JDO?、?EJB3?對象有兩種方式。一種選擇是表示層返回仍持久地的對象。另一種做法是讓業務層返回脫管對象。
?
????????????????????
?
l?????????是?POJO?具有事務性
?
用?spring?管理事務。對測試也很方便。
?
系統設計時需要考慮的五大因素:
?
?
?
1?、如何組織業務邏輯
2?、如何封裝業務邏輯,以及暴露給表示層及其他客戶程序調用的接口
3?、如何訪問數據庫
4?、如何處理短事務中的并發
5?、如何處理長期運行事務中的并發
| 決策 | 選項 |
| 業務邏輯封裝 | EJB Session Fa?ade?模式 POJO Fa?ade?模式 Exposed Domain Model?模式 |
| 數據庫訪問 | 直接使用JDBC iBATIS Hibernate JDO |
| 數據庫事務中的并發 | 不理睬該問題 悲觀鎖 樂觀鎖 可串行化隔離級別 |
| 長期運行事務中的并發 | 不理睬該問題 Pessimistic Offline Lock?模式 Optimistic Offline Lock?模式 |
| ? | ? |
以上5?個決策每個都有多種選項。基于EJB?的設計,它由會話bean?實現的過程式代碼組成,并使用JDBC?訪問數據庫。相比之下,基于POJO?的設計由對象模型組成,通過JDO?、Hibernate?等O/R?框架映射到數據庫,并用使用Spring?進行事務管理的POJO fa?ade?進行封裝。每種選項都有優缺點,這也決定了它只能適用于某種具體情況。每種選項都會在一個或多個方面做出一定的妥協,包括功能性、開發難易度、可維護性和可用性等,需要自己的應用程序作出最佳選擇。
?
封裝業務邏輯
?
業務邏輯的接口由那些可被表示層調用的類型(type?)和方法(method?) 組成。接口設計的要點是應當封裝多少業務邏輯的實現,并對表示層不可見。封裝隱藏了業務邏輯的實現細節,可以防止表示層受業務邏輯變化的影響,從而提升可 維護性。同時還需考慮怎樣處理事務、安全性和遠程調用等問題,因為通常這些都是業務邏輯接口代碼的職責。一般來說,業務層接口應保證對業務層的每個調用都 在事務中執行,以便保證數據庫的數據一致性。同樣的,業務層接口還要驗證調用者是否有足夠的權限來調用某種業務方法。此外,它還要負責處理某些遠程客戶 端。
?
???若存在表示層遠程訪問業務層API?的情況,盡量將業務層的API?設計成粗粒度的,這樣對業務層的調用越少,數據庫事務數量就越少,內存緩存對象的機會就越多。還能減少網絡來回傳輸次數。
?
?
單個數據庫?事務中的并發
完全事務腳本、樂觀(optimistic?)鎖、悲觀(pessimistic?)鎖?
完全事務腳本?是一種解決方案是使用完全和其他事務隔離的事務,用數據庫的話來說,就是隔離級別為serializable?(串行化)的事務(悲觀鎖的另一種方案)。數據庫保證:執行多個?serializable?事務的結果和一個個串行執行它們的結果一樣。serializable?事務避免了更新丟失、讀取不一致等問題。一些數據庫還提供了repeatable read?(能夠保持一致的重復讀取)和Read committed?的 隔離級別,完全隔離事務有兩個主要優點:一是使用簡單;二是避免了很多并發問題,包括修改丟失和讀取不一致的問題。完全隔離事務的主要缺點是開銷太大,降 低了性能和規模擴展性,不管有沒有并發更新,都需要額外開銷。而且,由于死鎖和其他并發相關問題,完全隔離事務比低隔離級別的事務的失敗頻率更高。用serializable?或者repeatable read?隔離級別的事務,這種方案不需要數據庫模式變化。因為數據庫的serializable?事務機制能夠處理并發更新,所以不需要用語句來鎖住記錄,也不需要維護版本號。在spring?中可以通過配置數據源的屬性“defaultTransactionIsolation?”為“SERIALIZABLE?”來實現。?
??樂觀鎖的工作原理?是讓應用程序檢查它即將更新的數據是否已被另一個事務修改(自該數據上次讀取以來)。實現樂觀鎖的一種常見做法是在每個表里添加一個版本字段,每次應用程序更新數據表記錄時就增加這個版本字段。每個UPDATE?語句中的WHERE?子句會根據上次讀取的值來判斷這個版本號是否改變。使用諸如JDO?和Hibernate?的持久層構架時,實現樂觀鎖更為容易,因為它們已將樂觀鎖作為配置選項提供。?
????應用程序或持久層框架有三種方法可以判斷一條記錄自從上次讀取出來后是否被修改過。?
????第一種方法是?用一個version?(版本)字段來跟蹤記錄修改狀況?,每次修改,version?都會遞增。事務只需要把原來讀出的?version?和當前version?進行比較,就可以判斷一條記錄是否被修改過。應用程序檢查和修改version?字段是比較簡單的做法,通常也是最好的做法。
第二種方法是?用時間戳字段,每次應用程序修改數據,時間戳也會更新?。 事務只需要把原來讀出的時間戳和當前時間戳進行比較,就可以判斷一條記錄是否被修改過。這個表結構也很容易實現,尤其是這種情況下,數據表經常已經有一個 時間戳字段來記錄用戶修改記錄的時間。然而,時間戳的問題是,如果兩個修改操作之間的時間差小于時鐘最小單位,那么一個事務可能覆蓋另一個事務的修改。所 以,只有在無法增加version?字段的遺留系統中,才應該使用時間戳,否則,盡量使用version?(版本)。
第三種方法是?把上次讀出的字段值和現有字段值進行比較?。這種方法最大的好處是,不需要引入version?或者時間戳字段,所以可以用在遺留系統中。這個方法的一個缺點是使得SQL UPDATE?更加復雜,因為WHERE?子句里面包含所有的字段的條件(具體原因我們后面會詳述)。還必須正確處理null?字段,可能比較復雜。比如,有一次我發現,一個持久層框架不能正確比較空字符串,因為Oracle?把空字符串認為是null?,這和Java?不一樣。我們在數據表里面增加了一個版本字段,解決了這個問題。
第三種方法的另一個缺點是,浮點數字段不能精確比較,浮點數字段的修改可能發現不了。由于這些問題,應用程序只有在別無選擇,無法應用版本和時間戳的情況下,才應該使用這種方法。
?
悲觀鎖的工作原理?是?當讀取某些記錄時,事務先鎖住這些記錄,這樣可以防止其他事務訪問這些數據記錄。具體細節要視數據庫而定,不過糟糕的是,并非所有數據庫都支持悲觀鎖。如果數據庫支持悲觀鎖,在直接執行SQL?語句的應用程序中,實現悲觀鎖非常容易。在JDO?或Hibernate?應用程序中使用悲觀鎖更為容易。JDO?以配置選項的方式提供悲觀鎖,而Hibernate?則提供一個簡單實用的API?,來鎖定對象。
????獲取鎖的機制是數據庫相關的,并非所有數據庫都支持。在Oracle?數據庫中,應用程序通過SELECT FOR UPDATE?語句鎖住選出的記錄,從而實現悲觀鎖。如果已經有事務鎖住記錄,執行SELECT FOR UPDATE?語句的事務就會被阻塞。如果其他事務更新、刪除或者試圖用SELECT FOR UPDATE?選取這些記錄,阻塞情況就會發生。事務一直阻塞,直到事務提交或者回滾。如果事務不想等待,可以采用SELECT FOR UPDATE NO WAIT?,如果不能立即鎖住記錄,就返回ORA-00054?錯誤。你還可以用SELECT FOR UPDATE WAIT?來指定等待時間。要注意的是:一些數據庫對SELECT FOR UPDATE?的用法有限制。例如,Oracle?里面,SELECT FOR UPDATE?只能用在頂層SQL?,而不能嵌在子查詢里面。還有一些SQL?特性不能和SELECT FOR UPDATE?一起使用。這些特性包括DISTINCT?,集合統計函數(max?、min?、sum?、count?),GROUP BY?。SELECT FOR UPDATE?也不能用在某些類型的view?和嵌套的SELECT?里面。
處理長事務中的并發
樂觀離線鎖(Optimistic Offline Lock)?模式、悲觀離線鎖(Pessimistic Offline Lock?)?模式?
樂觀離線鎖模式?是 擴展此前描述的樂觀鎖機制,在編輯過程的最后一個數據庫事務里,檢查數據自最初讀取后并未改變。例如,你可以使用共享數據表里的版本號字段實現這一機制。 在編輯過程開始時,應用程序先將版本號存儲在會話狀態里。然后,當用戶保存其更改時,應用程序進行檢查,保證會話狀態里保存的版本號和數據庫中的版本號一 致.?由于樂觀離線鎖模式只在用戶要保存修改后的數據時才進行檢測,在實現諸如“修改訂單”用例時,要用戶放棄好幾分鐘才完成的操作,用戶肯定會非常惱火,此時更好的選擇是使用悲觀離線鎖?.
悲觀離線鎖模式?是在編輯過程開始之初,就鎖定共享數據,以防止其他用戶編輯該共享數據。這種方式與此前描述的悲觀鎖機制類似,只不過這里鎖由應用程序而不是數據庫實現。由于每次只有一個用戶能編輯共享數據,因此可保證用戶能保存自己的修改。
通知并發更新失敗
當DAO?或者數據庫認為當前的兩個事務不能并發運行時,就會發生并發失敗。DAO?可以允許JDBC SQLException?傳播到DAO?的調用者。不過,有兩個原因說明這種做法不是一個好主意。拋出SQLException?的第一個問題是JDBC?相關。應用程序也可以使用JDO?這樣的持久層,但是拋出其他的非JDBC?異常。理想情況下,更高層的應用程序組件不應該知道底層訪問了數據庫。?SQLException?的另一個問題是,SQLException?是一個checked exception?(非Runtime Exception?,需要顯式聲明或者捕獲的異常),需要DAO?調用者或者捕捉SQLException?,或者在方法簽名上聲明繼續拋出?SQLException?,這種情況下,調用者代碼會變得雜亂。使用unchecked exceptions?(Runtime Exception?,不需要顯式聲明或者處理的異常)來報告并發失敗要好得多。
總結
- 上一篇: POJO和javabean的异同
- 下一篇: java的(PO,VO,TO,BO,DA