J2EE之DAO設計模式簡介與實例
在JAVA編程的時候, 有時候看起來非常直接的實現卻非要用設計模式轉若干個彎去實現他, 這似乎顯的很多余,但是采用一些成熟的設計模式,會使程序更加的健壯,松耦合以及好維護和擴展.
DAO 設計模式
背景:
根據數據源的不同,訪問數據的方法也會有所不同,訪問持久化的數據源,比如數據庫,也會由于其存儲類型的不同(關系數據庫,面向對象的數據庫,簡單文件儲存,其他方式)和提供商自定義數據類型的不同而有很大的區別。
出現的問題:
許 多投入使用的,J2EE WEB 應用程序在一些時候需要進行數據的持久化. 對于很多的WEB應用,數據的持久化存儲可以通過不同的機制來實現,文檔中 清楚的標出了這些用于訪問不同數據持久機制的API的不同之處. 還有一些其他的應用或許會訪問一些位于特有系統上的數據資源.比如,在大型機的系統之 上,也可能在輕量級的目錄訪問協議LDAP倉庫中,或者是其他什么系統. 還有就是,數據也可能是由諸如B2B這樣的外部集成系統服務,信用卡局服務,或 者其他服務來提供的.一般來說,程序使用一些共享的分布式組件來表示持久化數據.比如實體BEAN. 當一個程序中實體BEAN以比較直接的方式訪問持久 化數據時大多會考慮采用BEAN管理持久化方式(BMP)說明白點,就是程序中的實體BEAN包含有直接訪問持久化數據的代碼.另外一種情況,程序可以采 用容器管理持久化,你不需要寫任何代碼,而是讓容器自己來處理數據持久化的具體細節.
程序可以使用JDBC API 來訪問位于關系數據 庫中的數據. 他使得在諸如關系型數據庫這樣的持久化載體中,對數據進行標準的訪問和處理成為可能. 也使J2EE應用程序可以使用SQL語句作為標準的 訪問關系型數據庫語句. 然而,即便是都是關系型數據庫的環境下,由于不同的數據庫產品,也會導致SQL在使用上,語法和格式也各不相同.
對 于不同類型的數據持久化倉庫,差異甚至會更大. 訪問機制,API,以及一些其他特性,會因為他們是關系型數據庫,面向對象型數據庫還是一般的文件而大相 徑庭.需要訪問以前遺留下來的系統或者諸如大型主機,B2B這樣的專業系統中數據庫的應用程序,會經常使用到一些自己特有的API. 這些特有的數據源對 應用程序的編寫提出了很大的挑戰,而且很有可能在編寫的過程中造成程序代碼和數據訪問代碼間產生相互依賴性.當商業組件諸如:實體BEAN,會話 BEAN,以及servlets和JSP幫助對象這樣的表示組件需要訪問數據資源的時候,可以用標準的API來實現其數據庫的連接和數據的具體操作.但 是,如果把連接數據庫和數據的操作代碼和這些組件寫在一起的話,會導致這些組件和數據庫操作之間的耦合,這種耦合的存在,使得在應用程序中從當前數據源類 型遷移到另一種數據源類型變的十分困難和乏味. 如果數據源改變了,那么你的組件也不得不改變來適應新的數據源.
必要性:
1 像 bean管理實體bean, 會話 bean, servlets, 以及其他一些像jsp幫手對象這樣的組件,通常需要從持久化的數據庫或者原先遺留下來的系統以及 B2B, LDAP這樣的系統中提取或存儲數據。
2 用 于持久化儲存的API因他們的提供商的不同而各自不同。還有一些的數據源也可能有自己的一些特有的API或者是一些非標準的API。眾多類型的數據持久化 系統和載體,比如: 關系型數據庫,面向對象數據庫管理系統,XML文檔,簡單文件等等,使得API各不相同和性能各異。我們缺乏一種統一的API來對以 上的不同的系統或者文件載體進行操作。
3 組件通常使用特有的API從內部系統或者是遺留下來的系統來訪問或是提取數據。
4 當某些特定的訪問機制和API包含在這些組件中的時候,將直接影響他們的兼容性。
5 組件需要對現有的持久化儲存系統或者數據源的實現足夠透明,以便在向不同的產品,不同類型的儲存系統,和不同類型數據源中進行遷移的時候,變的簡單。
解決方案
使用數據訪問對象來抽象和封裝對數據源的所有訪問。數據訪問對象負責管理與數據源的連接,來獲取和儲存其中的數據。
數 據訪問對象實現與數據源相關的訪問機制。 數據源可以是關系型數據庫管理系統,可以是像B2B EXCHANGE這樣的內部服務,可以是LDAP庫,或者 也可以是通過CORBA IIOP 或者是低層sockets來訪問的商業服務. 依賴于DAO的商業組件只對他的客戶端暴露一些非常簡單的DAO外部接 口. DAO將數據源的實現細節對客戶端完全的隱藏了起來. 因為,暴露給客戶端的DAO接口在低層數據源的實現發生改變時并不會隨著改變,所以這種設計 模式使得DAO可以適應不同的數據儲存方式類型而不影響客戶端和商業組件.最主要的, DAO還在組件和數據源之間扮演著協調者的角色.
以下是DAO設計模式中各個模塊的解釋:
1 BusinessObject指的是數據客戶端,他通常需要去訪問數據源以獲得數據或儲存數據.一個BusinessObject除了訪問數據源的servlet或者helper bean之外也可以是會話BEAN,實體BEAN以及一些其他的JAVA對象.
2 DataAccessObject 是 這個設計模式的核心部分, DataAccessObject為BusinessObject抽象了底層的數據訪問實現的細節,使得訪問數據變得透 明. BusinessObject還將數據的裝載和儲存交給了DataAccessObject進行代理.
3 DataSource他 表示的是數據源的實現. 一個數據源可以四像關系型數據庫管理體統這樣的數據庫,可以是面向對象型的數據庫管理系統,可以是XML文檔,也可以是簡單文件 等等. 當然他也可以是其他的系統,(遺留系統,大型主機),可以是服務(B2B服務,信用卡局服務)或者是像LDAP這樣的數據庫等.
4 TransferObject他代表的是傳遞對象,一般用于數據的載體. DataAccessObject使用傳遞對象來將數據返回給客戶端. DataAccessObject也可以使用傳遞對象來從客戶端接受數據來將原先數據庫中的數據進行更新.
策略:
由 于每一個BusinessObject都有著相應的DAO,所以在BusinessObject,DAO,和底層實現之間是可以建立起確定的關系的(比如 在關系型數據庫中的表格)。一旦他們之間的關系建立了,我們就可以為這個應用使用專門定制出代碼生成器生成涉及到該應用所有的DAO的代碼。產生DAO的 元數據還可以通過開發人員定制的描述符文件來獲得.代碼生成器也可以通過自動的對數據庫進行檢查,然后按照實際情況來提供一些必要的DAO來訪問數據 庫. 如果對于DAO的要求過于復雜,則考慮使用第三方工具來為關系型數據庫提供對象到關系的映射.這些工具一般都包含圖形化的用戶界面可以方便的將商業 對象影射到持久化對象上,于是就可以定義DAO了。這些工具可以自動在影射一結束就自動的產生代碼,不但如此,他門可以提供一些附加的好處,比如結果緩 存,查詢緩存,與應用服務器的整合,于第三方產品的整和(分布試緩存)等等.
1 工廠模式策略:
DAO設計模式可以通過采用抽象工廠和工廠方法模式來邊的非常的靈活.
當底層數據儲存實現不需要發生改變時,該策略可以使用工廠方法設計模式實現,來產生應用中所需的DAO.
當 底層數據儲存實現不得不發生變化的時候, 我們可以用抽象工廠模式來實現這個策略. 就象在設計模式:可重用面向對象軟件的元素這本書中建議的那樣,抽象 工廠先創建然后再使用工廠方法的實現. 在當前情況,該策略可以提供一個抽象的DAO工廠對象(抽象工廠),用他來創建不同類型的具體DAO工廠.,每一 個工廠都各自支持一種不同的數據持久化儲存的實現. 一旦你為某個特定的實現獲得了具體的DAO工廠,你則可以用這個工廠來產生那個特定實現所支持和實現 的DAO對象.
優點與缺點:
DAO設計模式帶來的好處.
1 透明化:
商業對象可以在完全不知道數據源如何具體實現的情況下來使用數據源. 訪問數據源是透明的,因為實現細節已經被隱藏進了DAO.
2 遷移簡單化:
DAO 層的出現,使得應用程序向不同的數據庫實現進行遷移變的容易.商業對象可以對底層數據實現一無所知.這樣,遷移只涉及到了對DAO層的修改. 另外,如果 使用工廠策略,則使為每一種底層數據實現提供一個具體的工廠實現成為可能.在這種情況下,遷移到一種不同的數據實現,其實就相當于為這個應用程序再提供一 個新的工廠實現.
3 減少在商業對象中的編程難度.
由于DAO管理著所有的數據訪問細節,因而大大簡化了在商業對象和其他使用DAO的數據客戶端里的代碼.所有的實現細節相關的代碼比如(SQL 語句)都包含在DAO而不在商業對象中. 這樣使得代碼變的更加健壯而且大大提高了開發效率.
4 將所有的數據訪問都單獨集中到一層中去.
因為所有的數據訪問操作現在都已經被DAO所代理,所以這個單獨的數據訪問層可以被看作可以是將數據訪問實現和其余應用程序相互隔離的一層. 這樣的集中,使得應用程序可以更加容易的來維護和管理.
缺點:
5 對容器管理持久化無用
由 于EJB容器使用CMP(容器管理持久化)來管理實體BEAN. 容器會自動的為持久化儲存訪問提供服務.應用程序使用容器管理的實體BEAN則不需要 DAO層的參與.因為應用程序服務器本身就可以透明的提供這些功能.然而,DAO在組合式CMP和BMP需要的場合下還是有用的.
6 增加了多余的層.
由于DAO在數據客戶端和數據源之外多創建了一層對象,因而,需要對他進行設計和實現,來均衡這個設計模式的利弊. 但是,一般來說,采用此設計模式還是利大于弊的.
7 需要對類的相互繼承關系進行設計.
當 使用工廠策略的時候,具體工廠類的繼承關系和由這些工廠類生成的產品需要進行設計和實現. 我們需要仔細考慮這些多付出的工作是否真的可以產生出來更高的 靈活性. 使用這個策略會使設計變的更加復雜,然而,你可以先從工廠方法模式開始來實現這個策略,然后在需要的情況下再轉向抽象工廠
范例說明:
為DAO實現工廠類的策略
1 采用工廠方法設計模式
如果一個DAO 工廠只為一個數據庫的實現,(比如ORACLE)而創建很多的DAO的時候,實現該策略時,我們考慮采用工廠方法設計模式. 假設該工廠類創建了CustomerDAO, AccountDAO, OrderDAO 等一些對象。
2 使用抽象工廠設計模式:
如果考慮為三種不同類型的數據庫來實現這個策略,我們可以考慮采用抽象工廠設計模式. 假設. 這個工廠創建了CustomerDAO, AccountDAO, OrderDAO的一系列的DAO, 該策略運用了在抽象工廠中產生的工廠類中的工廠方法的實現.
代碼說明:
以下代碼舉例說明了DAO設計模式的具體實現:
我們以使用抽象工廠的設計模式來對付多種類型數據庫為例,在以下的例子中只具體列出CLOUDSCAPE 數據庫類型的DAO設計模式的具體實現,其他類型數據庫DAO設計模式的實現大同小異.
- // Abstract class DAO Factory
- public abstract class DAOFactory {
- // List of DAO types supported by the factory
- public static final int CLOUDSCAPE = 1;
- public static final int ORACLE = 2;
- public static final int SYBASE = 3;
- ...
- // There will be a method for each DAO that can be
- // created. The concrete factories will have to
- // implement these methods.
- // 所有實現該抽象工廠的工廠類中必須有的方法,用這些方法來創建具體的DAO類.
- public abstract CustomerDAO getCustomerDAO();
- public abstract AccountDAO getAccountDAO();
- public abstract OrderDAO getOrderDAO();
- //該抽象類的靜態方法,用他來創建其他具體的DAO工廠類
- public static DAOFactory getDAOFactory(
- int whichFactory) {
- switch (whichFactory) {
- case CLOUDSCAPE:
- return new CloudscapeDAOFactory();
- case ORACLE :
- return new OracleDAOFactory();
- case SYBASE :
- return new SybaseDAOFactory();
- ...
- default :
- return null;
- }
- }
- }
2 以下是Cloudscape DAO FACTORY 類的實現,在他里面實現了該類型數據庫的連接,以及實現了他所繼承的抽象工廠類中所必須實現的那些方法,在這些方法中創建具體的DAO對象.
- // Cloudscape concrete DAO Factory implementation
- import java.sql.*;
- public class CloudscapeDAOFactory extends DAOFactory {
- public static final String DRIVER=
- "COM.cloudscape.core.RmiJdbcDriver";
- public static final String DBURL=
- "jdbc:cloudscape:rmi://localhost:1099/CoreJ2EEDB";
- // method to create Cloudscape connections
- //建立Cloudscape 連接
- public static Connection createConnection() {
- // Use DRIVER and DBURL to create a connection
- // Recommend connection pool implementation/usage
- }
- //創建 CustomerDAO 對象 當然返回的是一個該類實現的接口,他的好處就是實現了實現細節的隱蔽.
- public CustomerDAO getCustomerDAO() {
- // CloudscapeCustomerDAO implements CustomerDAO
- return new CloudscapeCustomerDAO();
- }
- //創建 AccountDAO 對象 當然返回的是一個該類實現的接口,他的好處就是實現了實現細節的隱蔽.
- public AccountDAO getAccountDAO() {
- // CloudscapeAccountDAO implements AccountDAO
- return new CloudscapeAccountDAO();
- }
- //創建 OrderDAO 對象 當然返回的是一個該類實現的接口,他的好處就是實現了實現細節的隱蔽.
- public OrderDAO getOrderDAO() {
- // CloudscapeOrderDAO implements OrderDAO
- return new CloudscapeOrderDAO();
- }
- ...
- }
3 以下代碼就是具體DAO類實現的接口也就是CloudscapeCustomerDAO()實現的接口: CustomerDAO .在該接口中定義了所有的業務方法.
- // Interface that all CustomerDAOs must support
- public interface CustomerDAO {
- public int insertCustomer(...);
- public boolean deleteCustomer(...);
- public Customer findCustomer(...);
- public boolean updateCustomer(...);
- public RowSet selectCustomersRS(...);
- public Collection selectCustomersTO(...);
- ...
- }
4 以下CloudscapeCustomerDAO類實現的具體業務細節和數據操作細節, 他是要向客戶數據端隱蔽的.
- import java.sql.*;
- public class CloudscapeCustomerDAO implements
- CustomerDAO {
- public CloudscapeCustomerDAO() {
- // initialization
- }
- // The following methods can use
- // CloudscapeDAOFactory.createConnection()
- // to get a connection as required
- public int insertCustomer(...) {
- // Implement insert customer here.
- // Return newly created customer number
- // or a -1 on error
- }
- public boolean deleteCustomer(...) {
- // Implement delete customer here
- // Return true on success, false on failure
- }
- public Customer findCustomer(...) {
- // Implement find a customer here using supplied
- // argument values as search criteria
- // Return a Transfer Object if found,
- // return null on error or if not found
- }
- public boolean updateCustomer(...) {
- // implement update record here using data
- // from the customerData Transfer Object
- // Return true on success, false on failure or
- // error
- }
- public RowSet selectCustomersRS(...) {
- // implement search customers here using the
- // supplied criteria.
- // Return a RowSet.
- }
- public Collection selectCustomersTO(...) {
- // implement search customers here using the
- // supplied criteria.
- // Alternatively, implement to return a Collection
- // of Transfer Objects.
- }
- ...
- }
5 下面的代碼是數據客戶端向DAO中傳輸數據的, 他其實就是一個JAVABEAN;
- public class Customer implements java.io.Serializable {
- // member variables
- int CustomerNumber;
- String name;
- String streetAddress;
- String city;
- ...
- // getter and setter methods...
- ...
- }
6最后就是客戶數據端對這個設計的應用:
- ...
- // create the required DAO Factory
- DAOFactory cloudscapeFactory =
- DAOFactory.getDAOFactory(DAOFactory.DAOCLOUDSCAPE);
- // Create a DAO
- CustomerDAO custDAO =
- cloudscapeFactory.getCustomerDAO();
- // create a new customer
- int newCustNo = custDAO.insertCustomer(...);
- // Find a customer object. Get the Transfer Object.
- Customer cust = custDAO.findCustomer(...);
- // modify the values in the Transfer Object.
- cust.setAddress(...);
- cust.setEmail(...);
- // update the customer object using the DAO
- custDAO.updateCustomer(cust);
- // delete a customer object
- custDAO.deleteCustomer(...);
- // select all customers in the same city
- Customer criteria=new Customer();
- criteria.setCity("New York");
- Collection customersList =
- custDAO.selectCustomersTO(criteria);
- // returns customersList - collection of Customer
- // Transfer Objects. iterate through this collection to
- // get values.
1 創建一個抽象工廠類,他包含兩個重要的部分: 第一部分是 一些抽象方法,這些方法是所有實現該抽象工廠的具體工廠類所必須實現的. 第二部分 就是一個靜態方法,該方法來創建一個具體類型數據源的工廠對象,比如文中的CloudscapeDAOFactory().
2 然 后,分別創建各個類型數據源的工廠類,(本文以CloudscapeDAOFactory為例).在這個工廠類中里面也有兩個重要組成部分: 第一部分就 是實現在他繼承的那個抽象工廠類中的左右抽象方法,在該方法中創建具體的DAO對象(這些對象的類在第4不具體定義實現),本文中三個方法分別創建了3個 具體的DAO對象,當然為了實現細節的隱蔽,這些方法返回的是這些具體DAO類門實現的接口(這些接口在第3步實現).
3 定義具體DAO類的接口,并在接口中定義所有的業務方法,和數據操作方法.
4 定義具體的DAO類,在這個類中才是實際的業務方法,和數據的操作的實現.
5 定義數據傳輸對象,他是用來在客戶端和DAO之間傳遞數據的,他其實就是一個JAVABEAN.
6 完成以上5步之后我們就可以在數據客戶端使用以上由DAO設計模式定義好的各個類了(見最后一個代碼例子塊).
以上6步大家在編程的時需具體體會,一般來說,數據庫中的一個表就可以對應一個數據傳遞類也就是在第4步中定義的那個類,類中的屬性就是表中的字段,然后加上相應的GET,SET 方法. 然后再按模式和以上步驟來定義具體的類.
【編輯推薦】