新聞中心
在JAVA編程的時候, 有時候看起來非常直接的實現(xiàn)卻非要用設(shè)計模式轉(zhuǎn)若干個彎去實現(xiàn)他, 這似乎顯的很多余,但是采用一些成熟的設(shè)計模式,會使程序更加的健壯,松耦合以及好維護和擴展.

DAO 設(shè)計模式
背景:
根據(jù)數(shù)據(jù)源的不同,訪問數(shù)據(jù)的方法也會有所不同,訪問持久化的數(shù)據(jù)源,比如數(shù)據(jù)庫,也會由于其存儲類型的不同(關(guān)系數(shù)據(jù)庫,面向?qū)ο蟮臄?shù)據(jù)庫,簡單文件儲存,其他方式)和提供商自定義數(shù)據(jù)類型的不同而有很大的區(qū)別。
出現(xiàn)的問題:
許 多投入使用的,J2EE WEB 應(yīng)用程序在一些時候需要進行數(shù)據(jù)的持久化. 對于很多的WEB應(yīng)用,數(shù)據(jù)的持久化存儲可以通過不同的機制來實現(xiàn),文檔中 清楚的標出了這些用于訪問不同數(shù)據(jù)持久機制的API的不同之處. 還有一些其他的應(yīng)用或許會訪問一些位于特有系統(tǒng)上的數(shù)據(jù)資源.比如,在大型機的系統(tǒng)之 上,也可能在輕量級的目錄訪問協(xié)議LDAP倉庫中,或者是其他什么系統(tǒng). 還有就是,數(shù)據(jù)也可能是由諸如B2B這樣的外部集成系統(tǒng)服務(wù),信用卡局服務(wù),或 者其他服務(wù)來提供的.一般來說,程序使用一些共享的分布式組件來表示持久化數(shù)據(jù).比如實體BEAN. 當一個程序中實體BEAN以比較直接的方式訪問持久 化數(shù)據(jù)時大多會考慮采用BEAN管理持久化方式(BMP)說明白點,就是程序中的實體BEAN包含有直接訪問持久化數(shù)據(jù)的代碼.另外一種情況,程序可以采 用容器管理持久化,你不需要寫任何代碼,而是讓容器自己來處理數(shù)據(jù)持久化的具體細節(jié).
程序可以使用JDBC API 來訪問位于關(guān)系數(shù)據(jù) 庫中的數(shù)據(jù). 他使得在諸如關(guān)系型數(shù)據(jù)庫這樣的持久化載體中,對數(shù)據(jù)進行標準的訪問和處理成為可能. 也使J2EE應(yīng)用程序可以使用SQL語句作為標準的 訪問關(guān)系型數(shù)據(jù)庫語句. 然而,即便是都是關(guān)系型數(shù)據(jù)庫的環(huán)境下,由于不同的數(shù)據(jù)庫產(chǎn)品,也會導(dǎo)致SQL在使用上,語法和格式也各不相同.
對 于不同類型的數(shù)據(jù)持久化倉庫,差異甚至?xí)? 訪問機制,API,以及一些其他特性,會因為他們是關(guān)系型數(shù)據(jù)庫,面向?qū)ο笮蛿?shù)據(jù)庫還是一般的文件而大相 徑庭.需要訪問以前遺留下來的系統(tǒng)或者諸如大型主機,B2B這樣的專業(yè)系統(tǒng)中數(shù)據(jù)庫的應(yīng)用程序,會經(jīng)常使用到一些自己特有的API. 這些特有的數(shù)據(jù)源對 應(yīng)用程序的編寫提出了很大的挑戰(zhàn),而且很有可能在編寫的過程中造成程序代碼和數(shù)據(jù)訪問代碼間產(chǎn)生相互依賴性.當商業(yè)組件諸如:實體BEAN,會話 BEAN,以及servlets和JSP幫助對象這樣的表示組件需要訪問數(shù)據(jù)資源的時候,可以用標準的API來實現(xiàn)其數(shù)據(jù)庫的連接和數(shù)據(jù)的具體操作.但 是,如果把連接數(shù)據(jù)庫和數(shù)據(jù)的操作代碼和這些組件寫在一起的話,會導(dǎo)致這些組件和數(shù)據(jù)庫操作之間的耦合,這種耦合的存在,使得在應(yīng)用程序中從當前數(shù)據(jù)源類 型遷移到另一種數(shù)據(jù)源類型變的十分困難和乏味. 如果數(shù)據(jù)源改變了,那么你的組件也不得不改變來適應(yīng)新的數(shù)據(jù)源.
必要性:
1 像 bean管理實體bean, 會話 bean, servlets, 以及其他一些像jsp幫手對象這樣的組件,通常需要從持久化的數(shù)據(jù)庫或者原先遺留下來的系統(tǒng)以及 B2B, LDAP這樣的系統(tǒng)中提取或存儲數(shù)據(jù)。
2 用 于持久化儲存的API因他們的提供商的不同而各自不同。還有一些的數(shù)據(jù)源也可能有自己的一些特有的API或者是一些非標準的API。眾多類型的數(shù)據(jù)持久化 系統(tǒng)和載體,比如: 關(guān)系型數(shù)據(jù)庫,面向?qū)ο髷?shù)據(jù)庫管理系統(tǒng),XML文檔,簡單文件等等,使得API各不相同和性能各異。我們?nèi)狈σ环N統(tǒng)一的API來對以 上的不同的系統(tǒng)或者文件載體進行操作。
3 組件通常使用特有的API從內(nèi)部系統(tǒng)或者是遺留下來的系統(tǒng)來訪問或是提取數(shù)據(jù)。
4 當某些特定的訪問機制和API包含在這些組件中的時候,將直接影響他們的兼容性。
5 組件需要對現(xiàn)有的持久化儲存系統(tǒng)或者數(shù)據(jù)源的實現(xiàn)足夠透明,以便在向不同的產(chǎn)品,不同類型的儲存系統(tǒng),和不同類型數(shù)據(jù)源中進行遷移的時候,變的簡單。
解決方案
使用數(shù)據(jù)訪問對象來抽象和封裝對數(shù)據(jù)源的所有訪問。數(shù)據(jù)訪問對象負責(zé)管理與數(shù)據(jù)源的連接,來獲取和儲存其中的數(shù)據(jù)。
數(shù) 據(jù)訪問對象實現(xiàn)與數(shù)據(jù)源相關(guān)的訪問機制。 數(shù)據(jù)源可以是關(guān)系型數(shù)據(jù)庫管理系統(tǒng),可以是像B2B EXCHANGE這樣的內(nèi)部服務(wù),可以是LDAP庫,或者 也可以是通過CORBA IIOP 或者是低層sockets來訪問的商業(yè)服務(wù). 依賴于DAO的商業(yè)組件只對他的客戶端暴露一些非常簡單的DAO外部接 口. DAO將數(shù)據(jù)源的實現(xiàn)細節(jié)對客戶端完全的隱藏了起來. 因為,暴露給客戶端的DAO接口在低層數(shù)據(jù)源的實現(xiàn)發(fā)生改變時并不會隨著改變,所以這種設(shè)計 模式使得DAO可以適應(yīng)不同的數(shù)據(jù)儲存方式類型而不影響客戶端和商業(yè)組件.最主要的, DAO還在組件和數(shù)據(jù)源之間扮演著協(xié)調(diào)者的角色.
以下是DAO設(shè)計模式中各個模塊的解釋:
1 BusinessObject指的是數(shù)據(jù)客戶端,他通常需要去訪問數(shù)據(jù)源以獲得數(shù)據(jù)或儲存數(shù)據(jù).一個BusinessObject除了訪問數(shù)據(jù)源的servlet或者helper bean之外也可以是會話BEAN,實體BEAN以及一些其他的JAVA對象.
2 DataAccessObject 是 這個設(shè)計模式的核心部分, DataAccessObject為BusinessObject抽象了底層的數(shù)據(jù)訪問實現(xiàn)的細節(jié),使得訪問數(shù)據(jù)變得透 明. BusinessObject還將數(shù)據(jù)的裝載和儲存交給了DataAccessObject進行代理.
3 DataSource他 表示的是數(shù)據(jù)源的實現(xiàn). 一個數(shù)據(jù)源可以四像關(guān)系型數(shù)據(jù)庫管理體統(tǒng)這樣的數(shù)據(jù)庫,可以是面向?qū)ο笮偷臄?shù)據(jù)庫管理系統(tǒng),可以是XML文檔,也可以是簡單文件 等等. 當然他也可以是其他的系統(tǒng),(遺留系統(tǒng),大型主機),可以是服務(wù)(B2B服務(wù),信用卡局服務(wù))或者是像LDAP這樣的數(shù)據(jù)庫等.
4 TransferObject他代表的是傳遞對象,一般用于數(shù)據(jù)的載體. DataAccessObject使用傳遞對象來將數(shù)據(jù)返回給客戶端. DataAccessObject也可以使用傳遞對象來從客戶端接受數(shù)據(jù)來將原先數(shù)據(jù)庫中的數(shù)據(jù)進行更新.
策略:
由 于每一個BusinessObject都有著相應(yīng)的DAO,所以在BusinessObject,DAO,和底層實現(xiàn)之間是可以建立起確定的關(guān)系的(比如 在關(guān)系型數(shù)據(jù)庫中的表格)。一旦他們之間的關(guān)系建立了,我們就可以為這個應(yīng)用使用專門定制出代碼生成器生成涉及到該應(yīng)用所有的DAO的代碼。產(chǎn)生DAO的 元數(shù)據(jù)還可以通過開發(fā)人員定制的描述符文件來獲得.代碼生成器也可以通過自動的對數(shù)據(jù)庫進行檢查,然后按照實際情況來提供一些必要的DAO來訪問數(shù)據(jù) 庫. 如果對于DAO的要求過于復(fù)雜,則考慮使用第三方工具來為關(guān)系型數(shù)據(jù)庫提供對象到關(guān)系的映射.這些工具一般都包含圖形化的用戶界面可以方便的將商業(yè) 對象影射到持久化對象上,于是就可以定義DAO了。這些工具可以自動在影射一結(jié)束就自動的產(chǎn)生代碼,不但如此,他門可以提供一些附加的好處,比如結(jié)果緩 存,查詢緩存,與應(yīng)用服務(wù)器的整合,于第三方產(chǎn)品的整和(分布試緩存)等等.
1 工廠模式策略:
DAO設(shè)計模式可以通過采用抽象工廠和工廠方法模式來邊的非常的靈活.
當?shù)讓訑?shù)據(jù)儲存實現(xiàn)不需要發(fā)生改變時,該策略可以使用工廠方法設(shè)計模式實現(xiàn),來產(chǎn)生應(yīng)用中所需的DAO.
當 底層數(shù)據(jù)儲存實現(xiàn)不得不發(fā)生變化的時候, 我們可以用抽象工廠模式來實現(xiàn)這個策略. 就象在設(shè)計模式:可重用面向?qū)ο筌浖脑剡@本書中建議的那樣,抽象 工廠先創(chuàng)建然后再使用工廠方法的實現(xiàn). 在當前情況,該策略可以提供一個抽象的DAO工廠對象(抽象工廠),用他來創(chuàng)建不同類型的具體DAO工廠.,每一 個工廠都各自支持一種不同的數(shù)據(jù)持久化儲存的實現(xiàn). 一旦你為某個特定的實現(xiàn)獲得了具體的DAO工廠,你則可以用這個工廠來產(chǎn)生那個特定實現(xiàn)所支持和實現(xiàn) 的DAO對象.
優(yōu)點與缺點:
DAO設(shè)計模式帶來的好處.
1 透明化:
商業(yè)對象可以在完全不知道數(shù)據(jù)源如何具體實現(xiàn)的情況下來使用數(shù)據(jù)源. 訪問數(shù)據(jù)源是透明的,因為實現(xiàn)細節(jié)已經(jīng)被隱藏進了DAO.
2 遷移簡單化:
DAO 層的出現(xiàn),使得應(yīng)用程序向不同的數(shù)據(jù)庫實現(xiàn)進行遷移變的容易.商業(yè)對象可以對底層數(shù)據(jù)實現(xiàn)一無所知.這樣,遷移只涉及到了對DAO層的修改. 另外,如果 使用工廠策略,則使為每一種底層數(shù)據(jù)實現(xiàn)提供一個具體的工廠實現(xiàn)成為可能.在這種情況下,遷移到一種不同的數(shù)據(jù)實現(xiàn),其實就相當于為這個應(yīng)用程序再提供一 個新的工廠實現(xiàn).
3 減少在商業(yè)對象中的編程難度.
由于DAO管理著所有的數(shù)據(jù)訪問細節(jié),因而大大簡化了在商業(yè)對象和其他使用DAO的數(shù)據(jù)客戶端里的代碼.所有的實現(xiàn)細節(jié)相關(guān)的代碼比如(SQL 語句)都包含在DAO而不在商業(yè)對象中. 這樣使得代碼變的更加健壯而且大大提高了開發(fā)效率.
4 將所有的數(shù)據(jù)訪問都單獨集中到一層中去.
因為所有的數(shù)據(jù)訪問操作現(xiàn)在都已經(jīng)被DAO所代理,所以這個單獨的數(shù)據(jù)訪問層可以被看作可以是將數(shù)據(jù)訪問實現(xiàn)和其余應(yīng)用程序相互隔離的一層. 這樣的集中,使得應(yīng)用程序可以更加容易的來維護和管理.
缺點:
5 對容器管理持久化無用
由 于EJB容器使用CMP(容器管理持久化)來管理實體BEAN. 容器會自動的為持久化儲存訪問提供服務(wù).應(yīng)用程序使用容器管理的實體BEAN則不需要 DAO層的參與.因為應(yīng)用程序服務(wù)器本身就可以透明的提供這些功能.然而,DAO在組合式CMP和BMP需要的場合下還是有用的.
6 增加了多余的層.
由于DAO在數(shù)據(jù)客戶端和數(shù)據(jù)源之外多創(chuàng)建了一層對象,因而,需要對他進行設(shè)計和實現(xiàn),來均衡這個設(shè)計模式的利弊. 但是,一般來說,采用此設(shè)計模式還是利大于弊的.
7 需要對類的相互繼承關(guān)系進行設(shè)計.
當 使用工廠策略的時候,具體工廠類的繼承關(guān)系和由這些工廠類生成的產(chǎn)品需要進行設(shè)計和實現(xiàn). 我們需要仔細考慮這些多付出的工作是否真的可以產(chǎn)生出來更高的 靈活性. 使用這個策略會使設(shè)計變的更加復(fù)雜,然而,你可以先從工廠方法模式開始來實現(xiàn)這個策略,然后在需要的情況下再轉(zhuǎn)向抽象工廠
范例說明:
為DAO實現(xiàn)工廠類的策略
1 采用工廠方法設(shè)計模式
如果一個DAO 工廠只為一個數(shù)據(jù)庫的實現(xiàn),(比如ORACLE)而創(chuàng)建很多的DAO的時候,實現(xiàn)該策略時,我們考慮采用工廠方法設(shè)計模式. 假設(shè)該工廠類創(chuàng)建了CustomerDAO, AccountDAO, OrderDAO 等一些對象。
2 使用抽象工廠設(shè)計模式:
如果考慮為三種不同類型的數(shù)據(jù)庫來實現(xiàn)這個策略,我們可以考慮采用抽象工廠設(shè)計模式. 假設(shè). 這個工廠創(chuàng)建了CustomerDAO, AccountDAO, OrderDAO的一系列的DAO, 該策略運用了在抽象工廠中產(chǎn)生的工廠類中的工廠方法的實現(xiàn).
代碼說明:
以下代碼舉例說明了DAO設(shè)計模式的具體實現(xiàn):
我們以使用抽象工廠的設(shè)計模式來對付多種類型數(shù)據(jù)庫為例,在以下的例子中只具體列出CLOUDSCAPE 數(shù)據(jù)庫類型的DAO設(shè)計模式的具體實現(xiàn),其他類型數(shù)據(jù)庫DAO設(shè)計模式的實現(xiàn)大同小異.
- // 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.
- // 所有實現(xiàn)該抽象工廠的工廠類中必須有的方法,用這些方法來創(chuàng)建具體的DAO類.
- public abstract CustomerDAO getCustomerDAO();
- public abstract AccountDAO getAccountDAO();
- public abstract OrderDAO getOrderDAO();
- //該抽象類的靜態(tài)方法,用他來創(chuàng)建其他具體的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 類的實現(xiàn),在他里面實現(xiàn)了該類型數(shù)據(jù)庫的連接,以及實現(xiàn)了他所繼承的抽象工廠類中所必須實現(xiàn)的那些方法,在這些方法中創(chuàng)建具體的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
- }
- //創(chuàng)建 CustomerDAO 對象 當然返回的是一個該類實現(xiàn)的接口,他的好處就是實現(xiàn)了實現(xiàn)細節(jié)的隱蔽.
- public CustomerDAO getCustomerDAO() {
- // CloudscapeCustomerDAO implements CustomerDAO
- return new CloudscapeCustomerDAO();
- }
- //創(chuàng)建 AccountDAO 對象 當然返回的是一個該類實現(xiàn)的接口,他的好處就是實現(xiàn)了實現(xiàn)細節(jié)的隱蔽.
- public AccountDAO getAccountDAO() {
- // CloudscapeAccountDAO implements AccountDAO
- return new CloudscapeAccountDAO();
- }
- //創(chuàng)建 OrderDAO 對象 當然返回的是一個該類實現(xiàn)的接口,他的好處就是實現(xiàn)了實現(xiàn)細節(jié)的隱蔽.
- public OrderDAO getOrderDAO() {
- // CloudscapeOrderDAO implements OrderDAO
- return new CloudscapeOrderDAO();
- }
- ...
- }
3 以下代碼就是具體DAO類實現(xiàn)的接口也就是CloudscapeCustomerDAO()實現(xiàn)的接口: CustomerDAO .在該接口中定義了所有的業(yè)務(wù)方法.
- // 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類實現(xiàn)的具體業(yè)務(wù)細節(jié)和數(shù)據(jù)操作細節(jié), 他是要向客戶數(shù)據(jù)端隱蔽的.
- 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 下面的代碼是數(shù)據(jù)客戶端向DAO中傳輸數(shù)據(jù)的, 他其實就是一個JAVABEAN;
- public class Customer implements java.io.Serializable {
- // member variables
- int CustomerNumber;
- String name;
- String streetAddress;
- String city;
- ...
- // getter and setter methods...
- ...
- }
6最后就是客戶數(shù)據(jù)端對這個設(shè)計的應(yīng)用:
- ...
- // 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 創(chuàng)建一個抽象工廠類,他包含兩個重要的部分: 第一部分是 一些抽象方法,這些方法是所有實現(xiàn)該抽象工廠的具體工廠類所必須實現(xiàn)的. 第二部分 就是一個靜態(tài)方法,該方法來創(chuàng)建一個具體類型數(shù)據(jù)源的工廠對象,比如文中的CloudscapeDAOFactory().
2 然 后,分別創(chuàng)建各個類型數(shù)據(jù)源的工廠類,(本文以CloudscapeDAOFactory為例).在這個工廠類中里面也有兩個重要組成部分: 第一部分就 是實現(xiàn)在他繼承的那個抽象工廠類中的左右抽象方法,在該方法中創(chuàng)建具體的DAO對象(這些對象的類在第4不具體定義實現(xiàn)),本文中三個方法分別創(chuàng)建了3個 具體的DAO對象,當然為了實現(xiàn)細節(jié)的隱蔽,這些方法返回的是這些具體DAO類門實現(xiàn)的接口(這些接口在第3步實現(xiàn)).
3 定義具體DAO類的接口,并在接口中定義所有的業(yè)務(wù)方法,和數(shù)據(jù)操作方法.
4 定義具體的DAO類,在這個類中才是實際的業(yè)務(wù)方法,和數(shù)據(jù)的操作的實現(xiàn).
5 定義數(shù)據(jù)傳輸對象,他是用來在客戶端和DAO之間傳遞數(shù)據(jù)的,他其實就是一個JAVABEAN.
6 完成以上5步之后我們就可以在數(shù)據(jù)客戶端使用以上由DAO設(shè)計模式定義好的各個類了(見最后一個代碼例子塊).
以上6步大家在編程的時需具體體會,一般來說,數(shù)據(jù)庫中的一個表就可以對應(yīng)一個數(shù)據(jù)傳遞類也就是在第4步中定義的那個類,類中的屬性就是表中的字段,然后加上相應(yīng)的GET,SET 方法. 然后再按模式和以上步驟來定義具體的類.
當前標題:J2EE之DAO設(shè)計模式簡介與實例
轉(zhuǎn)載來于:http://fisionsoft.com.cn/article/ccogsdi.html


咨詢
建站咨詢
