Sung in Blog

                     一些技術(shù)文章 & 一些生活雜碎

          很多的J2EE應(yīng)用程序需要使用持久性數(shù)據(jù)(數(shù)據(jù)庫(kù)、文件等)。不同的程序,持久性存儲(chǔ)是各不相同的,并且用來(lái)訪問(wèn)這些不同的持久性存儲(chǔ)機(jī)制的API也有很大的不同。如果應(yīng)用程序要在不同的持久性存儲(chǔ)間遷移,這些訪問(wèn)特定持久存儲(chǔ)層的代碼將面臨重寫(xiě)。


          如何解決這個(gè)問(wèn)題?且看"DAO模式"


          數(shù)據(jù)訪問(wèn)對(duì)象(Data Acess Object) 模式



          一.環(huán)境
          根據(jù)數(shù)據(jù)源不同,數(shù)據(jù)訪問(wèn)也不同。根據(jù)存儲(chǔ)的類(lèi)型(關(guān)系數(shù)據(jù)庫(kù)、面向?qū)ο髷?shù)據(jù)庫(kù)、文件等等)和供應(yīng)商實(shí)現(xiàn)不同,持久性存儲(chǔ)(比如數(shù)據(jù)庫(kù))的訪問(wèn)差別也很大。


          二.問(wèn)題
          許多真是的J2EE應(yīng)用程序需要在一定程度上使用持久性數(shù)據(jù)。對(duì)于許多應(yīng)用程序,持久性存儲(chǔ)是使用不同的機(jī)制實(shí)現(xiàn)的,并且用來(lái)訪問(wèn)這些不同的持久性存儲(chǔ)機(jī)制的API也有很大的不同。
          比如,應(yīng)用程序使用實(shí)體bean(這里應(yīng)該是指BMP的bean,CMP的bean已大大降低了與RDBMS的耦合)的分布式組件來(lái)表示持久性數(shù)據(jù),或者使用JDBC API來(lái)訪問(wèn)駐留在某關(guān)系數(shù)據(jù)庫(kù)管理系統(tǒng)(RDBMS)中的數(shù)據(jù),這些組件中包含連接性性和數(shù)據(jù)訪問(wèn)代碼會(huì)引入這些組件與數(shù)據(jù)源實(shí)現(xiàn)之間的緊密耦合。組件中這類(lèi)代碼依賴(lài)性使應(yīng)用程序從某種數(shù)據(jù)源遷移到其他種類(lèi)的數(shù)據(jù)源將變得非常麻煩和困難。當(dāng)數(shù)據(jù)源變化時(shí),組件也需要改變,以便于能夠處理新類(lèi)型的數(shù)據(jù)源。


          (舉個(gè)例子來(lái)說(shuō),我們UPTEL系統(tǒng)是使用JDBC API對(duì) ORACLE數(shù)據(jù)庫(kù)進(jìn)行連接和數(shù)據(jù)訪問(wèn)的,這些JDBC API與SQL語(yǔ)句散布在系統(tǒng)中,當(dāng)我們需要將UPTEL遷移到其他RDBMS時(shí),比如曾經(jīng)遷移到INFORMIX,就面臨重寫(xiě)數(shù)據(jù)庫(kù)連接和訪問(wèn)數(shù)據(jù)的模塊。)


          三.作用力
          1.諸如bean管理的實(shí)體bean、會(huì)話(huà)bean、servlet等組件往往需要從持久性存儲(chǔ)數(shù)據(jù)源中檢索數(shù)據(jù),以及進(jìn)行數(shù)據(jù)存儲(chǔ)等操作。
          2.根據(jù)產(chǎn)品供應(yīng)商的不同,持久性存儲(chǔ)API差別也很大,這些API和其能力同樣根據(jù)存儲(chǔ)的類(lèi)型不同也有差別,這樣存在以下缺點(diǎn),即訪問(wèn)這些獨(dú)立系統(tǒng)的API很不統(tǒng)一。
          3.組件需要透明于實(shí)際的持久性存儲(chǔ)或者數(shù)據(jù)源實(shí)現(xiàn),以便于提供到不同供應(yīng)商產(chǎn)品、不同存儲(chǔ)類(lèi)型和不同數(shù)據(jù)源類(lèi)型的更容易的移植性。


          四.解決方案
          使用數(shù)據(jù)訪問(wèn)對(duì)象(DAO)模式來(lái)抽象和封裝所有對(duì)數(shù)據(jù)源的訪問(wèn)。DAO管理著與數(shù)據(jù)源的連接以便檢索和存儲(chǔ)數(shù)據(jù)。
          DAO實(shí)現(xiàn)了用來(lái)操作數(shù)據(jù)源的訪問(wèn)機(jī)制。數(shù)據(jù)源可以時(shí)RDBMS,LDAP,File等。依賴(lài)于DAO的業(yè)務(wù)組件為其客戶(hù)端使用DAO提供更簡(jiǎn)單的接口。DAO完全向客戶(hù)端隱藏了數(shù)據(jù)源實(shí)現(xiàn)細(xì)節(jié)。由于當(dāng)?shù)蛯訑?shù)據(jù)源實(shí)現(xiàn)變化時(shí),DAO向客戶(hù)端提供的接口不會(huì)變化,所有該模式允許DAO調(diào)整到不同的存儲(chǔ)模式,而不會(huì)影響其客戶(hù)端或者業(yè)務(wù)組件。重要的是,DAO充當(dāng)組件和數(shù)據(jù)源之間的適配器。


          (按照這個(gè)理論,如果我們UPTEL系統(tǒng)使用了DAO模式,就可以無(wú)縫的從ORACLE遷移到任何一個(gè)RDBMS了。夢(mèng)想總是很完美的,且看看DAO模式如何實(shí)現(xiàn))


          1.結(jié)構(gòu),圖1是表示DAO模式中各種關(guān)系的類(lèi)圖。


          此主題相關(guān)圖片如下:


          2.參與者和職責(zé)
          1)BusinessObject(業(yè)務(wù)對(duì)象)
          代表數(shù)據(jù)客戶(hù)端。正是該對(duì)象需要訪問(wèn)數(shù)據(jù)源以獲取和存儲(chǔ)數(shù)據(jù)。
          2)DataAccessObject(數(shù)據(jù)訪問(wèn)對(duì)象)
          是該模式的主要對(duì)象。DataAccessObject抽取該BusinessObject的低層數(shù)據(jù)訪問(wèn)實(shí)現(xiàn),以保證對(duì)數(shù)據(jù)源的透明訪問(wèn)。BusinessObject也可以把數(shù)據(jù)加載和存儲(chǔ)操作委托給DataAccessObject。
          3)DataSource(數(shù)據(jù)源)
          代表數(shù)據(jù)源實(shí)現(xiàn)。數(shù)據(jù)源可以是各RDBMSR數(shù)據(jù)庫(kù),OODBMS,XML文件等等。
          4)valueObject(值對(duì)象)
          代表用做數(shù)據(jù)攜帶著的值對(duì)象。DataAccessObject可以使用值對(duì)象來(lái)把數(shù)據(jù)返回給客戶(hù)端。
          DataAccessObject也許會(huì)接受來(lái)自于客戶(hù)端的數(shù)據(jù),其中這些用于更新數(shù)據(jù)源的數(shù)據(jù)存放于值對(duì)象中來(lái)傳遞。


          3.策略
          1).自動(dòng)DAO代碼產(chǎn)生策略
          因?yàn)槊總€(gè)BusinessObject對(duì)應(yīng)于一個(gè)特殊的DAO,因此有可能建立BusinessObject,DAO和低層實(shí)現(xiàn)(比如RDBMS中的表)之間的關(guān)系(映射)。一點(diǎn)這些關(guān)系(映射)已經(jīng)建立,我們就可以編寫(xiě)與應(yīng)用程序有館的代碼生成的簡(jiǎn)單工具了(什么?自己寫(xiě)GP程序?用ORM的附帶工具自動(dòng)生成不就完了,最多自己寫(xiě)幾個(gè)Adapter,牛人就是不同,啥都要自己寫(xiě)...),其中的工具可以產(chǎn)生該應(yīng)用程序需要的所有DAO代碼。
          如果DAO需求很復(fù)雜,我們可以采用第三方工具,其中這些工具提供對(duì)象到RDBMS數(shù)據(jù)庫(kù)的關(guān)系映射(這里指的是前面提到的ORM工具,全稱(chēng)是Object Relation Mapping,目前成熟的ORM工具有很多:Hibernate,OJB,Torque,TopLink等等)。
          這些工具通常包含GUI工具來(lái)把業(yè)務(wù)對(duì)象映射到持久性存儲(chǔ)對(duì)象,并且因而定義中間DAO。一旦這些映射完成,這些工具會(huì)自動(dòng)地生成代碼,并且也許會(huì)提供其他增值功能,比如結(jié)果緩沖、查詢(xún)緩沖、與應(yīng)用程序集成,以及與其他第三方產(chǎn)品(比如分布式緩沖)地繼承,等等。
          (增值服務(wù):Torque提供了結(jié)果緩沖,Hibernate提供了對(duì)Oracle數(shù)據(jù)庫(kù)SQL指令的優(yōu)化,OJB提供JDO API、OMDB API)


          2).數(shù)據(jù)訪問(wèn)對(duì)象的工廠策略
          通過(guò)調(diào)整抽象工廠和工廠方法模式,DAO模式可以達(dá)到很高的靈活度。
          當(dāng)?shù)蛯哟鎯?chǔ)不會(huì)隨著實(shí)現(xiàn)變化而變化時(shí),該策略可以使用工廠方法模式來(lái)實(shí)現(xiàn)該策略。以產(chǎn)生應(yīng)用程序需要的大量DAO。圖2是這種情況下的類(lèi)圖。


          此主題相關(guān)圖片如下:



          當(dāng)?shù)蛯哟鎯?chǔ)隨著實(shí)現(xiàn)變化而變化時(shí),該策略可以使用抽象工廠方法模式而實(shí)現(xiàn)。
          圖3是這種情況下的類(lèi)圖。


          此主題相關(guān)圖片如下:



          5.結(jié)果
          1).啟用透明性
          業(yè)務(wù)對(duì)象可以是使用數(shù)據(jù)源,而無(wú)須了解該數(shù)據(jù)源實(shí)現(xiàn)的具體細(xì)節(jié)。訪問(wèn)是透明的,原因是實(shí)現(xiàn)被隱藏在DAO的內(nèi)部。
          2).啟用更容易的遷移
          DAO層使應(yīng)用程序更加容易地遷移到一個(gè)不同的數(shù)據(jù)庫(kù)實(shí)現(xiàn)。業(yè)務(wù)對(duì)象不了解低層數(shù)據(jù)實(shí)現(xiàn)。因而,該遷移只涉及對(duì)DAO層的變化。更進(jìn)一步說(shuō),如果使用工廠策略,則有可能為每一個(gè)低層存儲(chǔ)實(shí)現(xiàn)提供一個(gè)具體工廠實(shí)現(xiàn)。在這種情況下,遷移到不同的遷移實(shí)現(xiàn)意味著給應(yīng)用程序提供一個(gè)新的工廠實(shí)現(xiàn)。
          3).減少業(yè)務(wù)對(duì)象中代碼復(fù)雜度
          由于DAO管理所有的數(shù)據(jù)訪問(wèn)復(fù)雜性,它可以簡(jiǎn)化業(yè)務(wù)對(duì)象和其他使用DAO的客戶(hù)端中的代碼。所有與實(shí)現(xiàn)有關(guān)的代碼(比如sql語(yǔ)句)都被包含在DAO中,而不是包含在業(yè)務(wù)對(duì)象中。這樣做提高了代碼的可讀性,已經(jīng)代碼生產(chǎn)效率。
          4).把所有的數(shù)據(jù)訪問(wèn)集中到一個(gè)獨(dú)立的層。
          因?yàn)樗械臄?shù)據(jù)訪問(wèn)操作現(xiàn)在被委托給DAO,所有單獨(dú)的數(shù)據(jù)訪問(wèn)層可以被看作把數(shù)據(jù)訪問(wèn)實(shí)現(xiàn)與應(yīng)用程序中的其他代碼相隔離的。這種集中化使應(yīng)用程序更容易地維護(hù)和管理。
          5).不適用于容器管理的持久性
          由于EJB容器用容器管理的持久性(CMP)來(lái)管理實(shí)體bean,該容器會(huì)自動(dòng)地服務(wù)所有的持久性存儲(chǔ)訪問(wèn)。使用容器管理的實(shí)體bean的應(yīng)用程序不需要DAO層,因?yàn)樵搼?yīng)用程序服務(wù)器透明地提供該功能。然而,當(dāng)需要組合使用CMP和BMP時(shí),DAO仍舊有用處。
          6).添加其他層
          DAO會(huì)在數(shù)據(jù)客戶(hù)端和數(shù)據(jù)源之間創(chuàng)建其他的對(duì)象層,其中該數(shù)據(jù)源需要被設(shè)計(jì)和實(shí)現(xiàn)以便于權(quán)衡該模式的好處。但是選擇本方法也會(huì)帶來(lái)額外的開(kāi)銷(xiāo)。
          7).需要類(lèi)層次設(shè)計(jì)
          在使用工廠策略時(shí),我們需要設(shè)計(jì)和實(shí)現(xiàn)具體工廠的層次,以及這些工廠產(chǎn)生的具體產(chǎn)品層次。如果能夠確保這種靈活性,則有必要考慮這種額外的工作。這樣做會(huì)增加設(shè)計(jì)的復(fù)雜性。然而,在實(shí)現(xiàn)該工廠策略時(shí),你可以首先考慮工廠方法模式,然后再根據(jù)需要過(guò)渡到抽象工廠。


          六.范例代碼
          1.實(shí)現(xiàn)數(shù)據(jù)訪問(wèn)對(duì)象模式
          范例9-4時(shí)表示Customer信息的持久性對(duì)象的DAO范例代碼。當(dāng)findCustomer()被調(diào)用時(shí),CloudscapeCustomerDAO創(chuàng)建一個(gè)Customer值對(duì)象。
          范例9-6是使用DAO的范例代碼。


          2.實(shí)現(xiàn)數(shù)據(jù)訪問(wèn)對(duì)象的工廠策略
          1)使用工廠方法模式
          2)使用抽象工廠模式
          范例代碼9-2是CloudscapeDAOFactory的范例代碼。
          范例代碼9-3中的CustomerDAO接口為Customer持久性對(duì)象定義了DAO方法,這些接口是被所有具體DAO實(shí)現(xiàn)來(lái)實(shí)現(xiàn)的,比如CloudscapeCustomerDAO、OracleCustomerDAO、已經(jīng)SybaseCustomerDAO。Account和OrederDAO接口也與此類(lèi)似。



          Example 9.1 Abstract DAOFactory Class


          // 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.
          public abstract CustomerDAO getCustomerDAO();
          public abstract AccountDAO getAccountDAO();
          public abstract OrderDAO getOrderDAO();
          ...


          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;
          }
          }
          }



          Example 9.2 Concrete DAOFactory Implementation for Cloudscape


          // 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
          public static Connection createConnection() {
          // Use DRIVER and DBURL to create a connection
          // Recommend connection pool implementation/usage
          }
          public CustomerDAO getCustomerDAO() {
          // CloudscapeCustomerDAO implements CustomerDAO
          return new CloudscapeCustomerDAO();
          }
          public AccountDAO getAccountDAO() {
          // CloudscapeAccountDAO implements AccountDAO
          return new CloudscapeAccountDAO();
          }
          public OrderDAO getOrderDAO() {
          // CloudscapeOrderDAO implements OrderDAO
          return new CloudscapeOrderDAO();
          }
          ...
          }



          Example 9.3 Base DAO Interface for Customer


          // 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 selectCustomersVO(...);
          ...
          }



          Example 9.4 Cloudscape DAO Implementation for Customer


          // CloudscapeCustomerDAO implementation of the
          // CustomerDAO interface. This class can contain all
          // Cloudscape specific code and SQL statements.
          // The client is thus shielded from knowing
          // these implementation details.


          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 value object if found,
          // return null on error or if not found
          }


          public boolean updateCustomer(...) {
          // implement update record here using data
          // from the customerData value 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 selectCustomersVO(...) {
          // implement search customers here using the
          // supplied criteria.
          // Alternatively, implement to return a Collection
          // of value objects.
          }
          ...
          }



          Example 9.5 Customer value Object


          public class Customer implements java.io.Serializable {
          // member variables
          int CustomerNumber;
          String name;
          String streetAddress;
          String city;
          ...


          // getter and setter methods...
          ...
          }



          Example 9.6 Using a DAO and DAO Factory ?Client Code


          ...
          // 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 value object.
          Customer cust = custDAO.findCustomer(...);


          // modify the values in the value 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("廣州");
          Collection customersList =
          custDAO.selectCustomersVO(criteria);
          // returns customersList - collection of Customer
          // value objects. iterate through this collection to
          // get values.


          ...



          七.相關(guān)模式
          1.值對(duì)象
          2.工廠方法
          3.代理

          posted on 2005-10-15 16:48 Sung 閱讀(157) 評(píng)論(0)  編輯  收藏 所屬分類(lèi): Java
          主站蜘蛛池模板: 东乡族自治县| 广东省| 马鞍山市| 额敏县| 余姚市| 白城市| 东丽区| 凤冈县| 衢州市| 安西县| 嵊泗县| 安顺市| 宝鸡市| 乌兰察布市| 恩平市| 万山特区| 红原县| 长顺县| 天峨县| 昌邑市| 年辖:市辖区| 区。| 读书| 鄂伦春自治旗| 望城县| 英吉沙县| 五寨县| 阿拉善左旗| 沽源县| 临泽县| 西吉县| 徐闻县| 手游| 日照市| 额济纳旗| 思南县| 武清区| 祁门县| 枣庄市| 筠连县| 绥棱县|