JAVA DAO編程模式 (zhuan)
在最近的18個月,我和一個優秀的軟件開發團隊一起工作,開發定制基于WEB的供應鏈管理應用程序.我們的應用程序訪問廣泛的持久層數據,包括出貨狀態,供應鏈制度,庫存,貨物發運,項目管理數據,和用戶屬性等.我們使用JDBC API連接我們公司的各種數據庫平臺,并且在整個應用程序中應用了DAO設計模式.
通過在整個應用程序中應用數據訪問對象(DAO)設計模式使我們能夠把底層的數據訪問邏輯和上層的商務邏輯分開.我們為每個數據源創建了提供CRUD(創建,讀取,更新,刪除)操作的DAO類.
在本文中,我將向你介紹DAO的實現策略以及創建更好的DAO類的技術.我會明確的介紹日志記錄,異常處理,和事務劃分三項技術.你將學在你的DAO類中怎樣把這三種技術結合在一起.這篇文章假設你熟悉JDBC API,SQL和關系性數據庫編程.
我們先來回顧一下DAO設計模式和數據訪問對象.
DAO基礎
DAO模式是標準的J2EE設計模式之一.開發人員使用這個模式把底層的數據訪問操作和上層的商務邏輯分開.一個典型的DAO實現有下列幾個組件:
1. 一個DAO工廠類;
2. 一個DAO接口;
3. 一個實現DAO接口的具體類;
4. 數據傳遞對象(有些時候叫做值對象).
具體的DAO類包含了從特定的數據源訪問數據的邏輯。在下面的這段中你將學到設計和實現數據訪問對象的技術。
事務劃分:
關于DAO要記住的一件重要事情是它們是事務性對象。每個被DAO執行的操作(象創建,更新、或刪除數據)都是和事務相關聯的。同樣的,事務劃分(transaction demarcation)的概念是特別重要的。
事務劃分是在事務界定定義中的方式。J2EE規范為事務劃分描述了兩種模式:編程性事務(programmatic)和聲明性事務(declarative)。下表是對這兩種模式的拆分:
聲明性事務劃分 | 編程性事務劃分 |
程序員使用EJB的布署描述符聲明事務屬性 | 程序員擔負編寫事務邏輯代碼的責任。 |
運行時環境(EJB容器)使用這些屬性來自動的管理事務。 | 應用程序通過一個API接口來控制事務。 |
我將把注意力集中的編程性事務劃分上。
象前面的介紹一樣,DAOs是一些事務對象。一個典型的DAO要執行象創建、更新、和刪除這的事務性操作。在設計一個DAO時,首先要問自己如下問題:
1、 事務將怎樣開始?
2、 事務將怎樣結束?
3、 那個對象將承擔起動一個事務的責任?
4、 那個對象將承擔結束一個事務的責任?
5、 DAO應該承擔起動和結束事務的責任?
6、 應用程序需要交叉訪問多個DAO嗎?
7、 一個事務包含一個DAO還是多個DAO?
8、 一個DAO包含其它的DAO中的方法嗎?
回答這些問題將有助于你為DAO對象選擇最好的事務劃分策略。對ADO中的事務劃分有兩個主要的策略。一種方法是使用DAO承擔事務劃分的責任;另一種是延期性事務,它把事務劃分到調用DAO對象的方法中。如果你選擇前者,你將要在DAO類中嵌入事務代碼。如果你選擇后者,事務代碼將被寫在DAO類的外部。我們將使用簡單的代碼實例來更好的理解這兩種方法是怎樣工作的。
實例1展示了一個帶有兩種數據操作的DAO:創建(create)和更新(update):
public void createWarehouseProfile(WHProfile profile); public void updateWarehouseStatus(WHIdentifier id, StatusInfo status); |
實例2展示了一個簡單的事務,事務劃分代碼是在DAO類的外部。注意:在這個例子中的調用者把多個DOA操作組合到這個事務中。
tx.begin(); // start the transaction dao.createWarehouseProfile(profile); dao.updateWarehouseStatus(id1, status1); dao.updateWarehouseStatus(id2, status2); tx.commit(); // end the transaction |
這種事務事務劃分策略對在一個單一事務中訪問多個DAO的應用程序來說尤為重要。
你即可使用JDBC API也可以使用Java 事務API(JTA)來實現事務的劃分。JDBC事務劃分比JTA事務劃分簡單,但是JTA提供了更好的靈活性。在下面的這段中,我們會進一步的看事務劃分機制。
使用JDBC的事務劃分
JDBC事務是使用Connection對象來控制的。JDBC的連接接口(java.sql.Connection)提供了兩種事務模式:自動提交和手動提交。Java.sql.Connection為控制事務提供了下列方法:
.public void setAutoCommit(Boolean) .public Boolean getAutoCommit() .public void commit() .public void rollback() |
實例3展示怎樣使用JDBC API來劃分事務:
import java.sql.*; import javax.sql.*; // ... DataSource ds = obtainDataSource(); Connection conn = ds.getConnection(); conn.setAutoCommit(false); // ... pstmt = conn.prepareStatement(";UPDATE MOVIES ...";); pstmt.setString(1, ";The Great Escape";); pstmt.executeUpdate(); // ... conn.commit(); // ... |
使用JDBC事務劃分,你能夠把多個SQL語句組合到一個單一事務中。JDBC事務的缺點之一就是事務范圍被限定在一個單一的數據庫連接中。一個JDBC事務不能夠跨越多個數據庫。接下來,我們會看到怎樣使用JTA來做事務劃分的。因為JTA不象JDBC那樣被廣泛的了解,所以我首先概要的介紹一下JTA。
JTA概要介紹
Java事務API(JTA;Java Transaction API)和它的同胞Java事務服務(JTS;Java Transaction Service),為J2EE平臺提供了分布式事務服務。一個分布式事務(distributed transaction)包括一個事務管理器(transaction manager)和一個或多個資源管理器(resource manager)。一個資源管理器(resource manager)是任意類型的持久化數據存儲。事務管理器(transaction manager)承擔著所有事務參與單元者的相互通訊的責任。下車站顯示了事務管理器和資源管理的間的關系。
JTA事務比JDBC事務更強大。一個JTA事務可以有多個參與者,而一個JDBC事務則被限定在一個單一的數據庫連接。下列任一個Java平臺的組件都可以參與到一個JTA事務中:
.JDBC連接
.JDO PersistenceManager 對象
.JMS 隊列
.JMS 主題
.企業JavaBeans(EJB)
.一個用J2EE Connector Architecture 規范編譯的資源分配器。
使用JTA的事務劃分
要用JTA來劃分一個事務,應用程序調用javax.transaction.UserTransaction接口中的方法。示例4顯示了一個典型的JNDI搜索的UseTransaction對象。
import javax.transaction.*; import javax.naming.*; // ... InitialContext ctx = new InitialContext(); Object txObj = ctx.lookup(";java:comp/UserTransaction";); UserTransaction utx = (UserTransaction) txObj; |
應用程序有了UserTransaction對象的引用之后,就可以象示例5那樣來起動事務。
utx.begin(); // ... DataSource ds = obtainXADataSource(); Connection conn = ds.getConnection(); pstmt = conn.prepareStatement(";UPDATE MOVIES ...";); pstmt.setString(1, ";Spinal Tap";); pstmt.executeUpdate(); // ... utx.commit(); // ... |
當應用程序調用commit()時,事務管理器使用兩段提交協議來結束事務。JTA事務控制的方法:
.javax.transaction.UserTransaction接口提供了下列事務控制方法:
.public void begin() .public void commit() .public void rollback() .public void getStatus() .public void setRollbackOnly() .public void setTransactionTimeout(int) |
應用程序調用begin()來起動事務,即可調用commit()也可以調用rollback()來結束事務。
使用JTA和JDBC
開發人員經常使用JDBC來作為DAO類中的底層數據操作。如果計劃使用JTA來劃分事務,你將需要一個實現了javax.sql.XADataSource,javax.sql.XAConnection和javax.sql.XAResource接口JDBC的驅動。實現了這些接口的驅動將有能力參與到JTA事務中。一個XADataSource對象是一個XAConnection對象的工廠。XAConnections是參與到JTA事務中的連接。
你需要使用應用程序服務器管理工具來建立XADataSource對象。對于特殊的指令請參考應用程序服務器文檔和JDBC驅動文檔。
J2EE應用程序使用JNDI來查找數據源。一旦應用程序有了一個數據源對象的引用,這會調用javax.sql.DataSource.getConnection()來獲得數據庫的連接。
XA連接區別于非XA連接。要記住的是XA連接是一個JTA事務中的參與者。這就意味著XA連接不支持JDBC的自動提交特性。也就是說應用程序不必在XA連接上調用java.sql.Connection.commit()或java.sql.Connection.rollback()。相反,應用程序應該使用UserTransaction.begin()、UserTransaction.commit()和UserTransaction.rollback().
選擇最好的方法
我們已經討論了JDBC和JTA是怎樣劃分事務的。每一種方法都有它的優點,回此你需要決定為你的應用程序選擇一個最適應的方法。 在我們團隊許多最近的對于事務劃分的項目中使用JDBC API來創建DAO類。這DAO類總結如下:
.事務劃分代碼被嵌入到DAO類內部
.DAO類使用JDBC API來進行事務劃分
.調用者沒有劃分事務的方法
.事務范圍被限定在一個單一的JDBC連接
JDBC事務對復雜的企業應用程序不總是有效的。如果你的事務將跨越多個DAO對象或多個數據庫,那么下面的實現策略可能會更恰當:
.用JTA對事務進行劃分
.事務劃分代碼被DAO分開
.調用者承擔劃分事務的責任
.DAO參與一個全局的事務中
JDBC方法由于它的簡易性而具有吸引力,JTA方法提供了更多靈活性。你選擇什么樣的實現將依賴于你的應用程序的特定需求。
日志記錄和DAO
一個好的DAO實現類將使用日志記錄來捕獲有關它在運行時的行為細節。你可以選擇記錄異常、配置信息、連接狀態、JDBC驅動程序的元數據或查詢參數。日志對開發整個階段都是有益的。我經常檢查應用程序在開發期間、測試期間和產品中的日志記錄。
在這段中,我們將展現一段如何把Jakarta Commaons Logging結合中一個DAO中的例子。在我們開始之前,讓我們先回顧一些基礎知識。
選擇一個日志例庫
許多開發人員使用的基本日志形式是:System.out.println和System.err.println.Println語句。這種形式快捷方便,但它們不能提供一個完整的日志系統的的能力。下表列出了Java平臺的日志類庫:
日志類庫 | 開源嗎? | URL |
Java.util.logging | 否 | http://java.sun.com/j2ee |
Jakarta Log4j | 是 | http://hajarta.apache.org/log4j/ |
Jakarta Commons Logging | 是 | http:/Jakarta.apache.org/commons/logging.html |
Java.util.logging是J2SE1.4平臺上的標準的API。但是,大多數開發人員都認為Jakarta Log4j提供了更大的功能性和靈活性。Log4j超越java.util.logging的優點之一就是它支持J2SE1.3和J2SE1.4平臺。
Jakarta Commons Logging能夠被用于和java.util.loggin或Jakarta Log4j一起工作。Commons Logging是一個把你的應用程序獨立于日志實現的提取層。使用Commons Logging你能夠通過改變一個配置文件來與下面的日志實現來交換數據。Commons Logging被用于JAKARTA Struts1.1和Jakarta HttpClient2.0中。
一個日志示例
示例7顯示了在一個DOA類中怎樣使用Jakarta Commons Logging
import org.apache.commons.logging.*; class DocumentDAOImpl implements DocumentDAO { static private final Log log = LogFactory.getLog(DocumentDAOImpl.class); public void deleteDocument(String id) { // ... log.debug(";deleting document: "; + id); // ... try { // ... data operations ... } catch (SomeException ex) { log.error(";Unable to delete document"; ex); // ... handle the exception ... } } } |
日志是評估應用程序的基本部分。如果你在一個DAO中遇到了失敗,日志經常會為理解發生的什么錯誤提供最好的信息。把日志結合到你的DAO中,確保得到調試和解決問題的有效手段。
DAO中的異常處理
我們已經看了事務劃分和日志記錄,并且現在對于它們是怎樣應用于數據訪問對象的有一個深入的理解。我們第三部分也是最后要討論的是異常處理。下面的一些簡單的異常處理方針使用你的DAO更容易使用,更加健壯和更具有可維護性。
在實現DAO模式的時候,要考濾下面的問題:
.在DAO的public接口中的方法將拋出被檢查的異常嗎?
.如果是,將拋出什么樣的檢查性異常?
.在DAO實現類中怎能樣處理異常。
在用DAO模式工作的過程中,我們的團隊為異常處理開發了一組方針。下面的這些方針會很大程度的改善你的DAO:
.DAO方法應該拋出有意義的異常。
.DAO方法不應該拋出java.lang.Exception異常。因為java.lang.Exception太一般化,它不能包含有關潛在問題的所有信息。
.DAO方法不應該拋出java.sql.SQLException異常。SQLException是一個底層的JDBC異常,DAO應用努力封裝JDBC異常而不應該把JDBC異常留給應用程序的其它部分。
.在DAO接口中的方法應該只拋出調用者期望處理的檢查性異常。如果調用者不能用適當的方法來處理異常,考濾拋出不檢查性(運行時run-time)異常。
.如果你的數據訪問代碼捕獲了一個異常,不可要忽略它。忽略捕獲異常的DAO是很處理的。
.使用異常鏈把底層的異常傳遞給高層的某個處理器。
.考濾定義一個標準的DAO異常類。Spring框架提供了一個優秀的預定義的DAO異常類的集合。
看Resources,查看有異常和異常處理技術的更詳細信息。
實現示例:MovieDAO
MoveDAO是一個示范了在這篇文章中所討論的所有技術,包括事務劃分、日志記錄和異常處理。你會在Resources段找到MovieDAO的源代碼。它被分下面的三個包:
.daoexamples.exception .daoexamples.move .daoexamples.moviedemo |
這個DAO模式的實現由下面的類和接口組成:
.daoexamples.movie.MovieDAOFactory .daoexamples.movie.MovieDAO .daoexamples.movie.MovieDAOImpl .daoexamples.movie.MovieDAOImplJTA .daoexamples.movie.Movie .daoexamples.movie.MovieImple .daoexamples.movie.MovieNotFoundException .daoexamples.movie.MovieUtil |
MovieDAO接口定義了DAO的數據操作。這個接口有如下五個方法:
.public Movie findMovieById(String id) .public java.util.Collection findMoviesByYear(String year) .public void deleteMovie(String id) .public Movie createMovie(String rating,String year,String title) .public void updateMovie(String id,String rating,String year,String title) |
daoexamples.movie包包含了兩個MovieDAO接口的實現。每個實現使用了一個同的事務劃分方法,如下表所示:
? | MovieDAOImpl | MovieDAOImplJTA |
實現了MovieDAO接口嗎? | Yes | Yes |
通過JNDI獲得DataSource嗎? | Yes | Yes |
從一個DataSource獲得java.sql.Connection對象嗎? | Yes | Yes |
DAO界定內部的事務嗎? | Yes | No |
使用JDBC事務嗎? | Yes | No |
使用一個XA DataSource嗎? | No | Yes |
分擔JTA事務嗎? | No | Yes |
MovieDAO 示范應用程序
這個示范應用程序是一個叫做daoexamples.moviedemo.DemoServlet.DemoServlet的servlet類,它使用Movie DAO來查詢和更新一個表中的movie數據。
這個servlet示范了把JTA感知的MovieDAO和Java消息服務組合到一個單一的事務中,如示例8所示:
UserTransaction utx = MovieUtil.getUserTransaction(); utx.begin(); batman = dao.createMovie(";R"; ";2008"; ";Batman Reloaded";); publisher = new MessagePublisher(); publisher.publishTextMessage(";I’ll be back";); dao.updateMovie(topgun.getId(), ";PG-13"; topgun.getReleaseYear(), topgun.getTitle()); dao.deleteMovie(legallyblonde.getId()); utx.commit(); |
要運行這個范例應用程序,在你的應用程序服務器中配置一個XA 數據源和一個非XA數據源。然后布署daoexamples.ear文件。這個應用程序將運行在任何與J2EE兼容的應用程序服務器。
如何解決這個問題?且看"DAO模式"
數據訪問對象(Data Acess Object) 模式
一.環境
根據數據源不同,數據訪問也不同。根據存儲的類型(關系數據庫、面向對象數據庫、文件等等)和供應商實現不同,持久性存儲(比如數據庫)的訪問差別也很大。
二.問題
許多真是的J2EE應用程序需要在一定程度上使用持久性數據。對于許多應用程序,持久性存儲是使用不同的機制實現的,并且用來訪問這些不同的持久性存儲機制的API也有很大的不同。
比如,應用程序使用實體bean(這里應該是指BMP的bean,CMP的bean已大大降低了與RDBMS的耦合)的分布式組件來表示持久性數據,或者使用JDBC API來訪問駐留在某關系數據庫管理系統(RDBMS)中的數據,這些組件中包含連接性性和數據訪問代碼會引入這些組件與數據源實現之間的緊密耦合。組件中這類代碼依賴性使應用程序從某種數據源遷移到其他種類的數據源將變得非常麻煩和困難。當數據源變化時,組件也需要改變,以便于能夠處理新類型的數據源。
(舉個例子來說,我們UPTEL系統是使用JDBC API對 ORACLE數據庫進行連接和數據訪問的,這些JDBC API與SQL語句散布在系統中,當我們需要將UPTEL遷移到其他RDBMS時,比如曾經遷移到INFORMIX,就面臨重寫數據庫連接和訪問數據的模塊。)
三.作用力
1.諸如bean管理的實體bean、會話bean、servlet等組件往往需要從持久性存儲數據源中檢索數據,以及進行數據存儲等操作。
2.根據產品供應商的不同,持久性存儲API差別也很大,這些API和其能力同樣根據存儲的類型不同也有差別,這樣存在以下缺點,即訪問這些獨立系統的API很不統一。
3.組件需要透明于實際的持久性存儲或者數據源實現,以便于提供到不同供應商產品、不同存儲類型和不同數據源類型的更容易的移植性。
四.解決方案
使用數據訪問對象(DAO)模式來抽象和封裝所有對數據源的訪問。DAO管理著與數據源的連接以便檢索和存儲數據。
DAO實現了用來操作數據源的訪問機制。數據源可以時RDBMS,LDAP,File等。依賴于DAO的業務組件為其客戶端使用DAO提供更簡單的接口。DAO完全向客戶端隱藏了數據源實現細節。由于當低層數據源實現變化時,DAO向客戶端提供的接口不會變化,所有該模式允許DAO調整到不同的存儲模式,而不會影響其客戶端或者業務組件。重要的是,DAO充當組件和數據源之間的適配器。
(按照這個理論,如果我們UPTEL系統使用了DAO模式,就可以無縫的從ORACLE遷移到任何一個RDBMS了。夢想總是很完美的,且看看DAO模式如何實現)
1.結構,圖1是表示DAO模式中各種關系的類圖。
此主題相關圖片如下:

2.參與者和職責
1)BusinessObject(業務對象)
代表數據客戶端。正是該對象需要訪問數據源以獲取和存儲數據。
2)DataAccessObject(數據訪問對象)
是該模式的主要對象。DataAccessObject抽取該BusinessObject的低層數據訪問實現,以保證對數據源的透明訪問。BusinessObject也可以把數據加載和存儲操作委托給DataAccessObject。
3)DataSource(數據源)
代表數據源實現。數據源可以是各RDBMSR數據庫,OODBMS,XML文件等等。
4)valueObject(值對象)
代表用做數據攜帶著的值對象。DataAccessObject可以使用值對象來把數據返回給客戶端。
DataAccessObject也許會接受來自于客戶端的數據,其中這些用于更新數據源的數據存放于值對象中來傳遞。
3.策略
1).自動DAO代碼產生策略
因為每個BusinessObject對應于一個特殊的DAO,因此有可能建立BusinessObject,DAO和低層實現(比如RDBMS中的表)之間的關系(映射)。一點這些關系(映射)已經建立,我們就可以編寫與應用程序有館的代碼生成的簡單工具了(什么?自己寫GP程序?用ORM的附帶工具自動生成不就完了,最多自己寫幾個Adapter,牛人就是不同,啥都要自己寫...),其中的工具可以產生該應用程序需要的所有DAO代碼。
如果DAO需求很復雜,我們可以采用第三方工具,其中這些工具提供對象到RDBMS數據庫的關系映射(這里指的是前面提到的ORM工具,全稱是Object Relation Mapping,目前成熟的ORM工具有很多:Hibernate,OJB,Torque,TopLink等等)。
這些工具通常包含GUI工具來把業務對象映射到持久性存儲對象,并且因而定義中間DAO。一旦這些映射完成,這些工具會自動地生成代碼,并且也許會提供其他增值功能,比如結果緩沖、查詢緩沖、與應用程序集成,以及與其他第三方產品(比如分布式緩沖)地繼承,等等。
(增值服務:Torque提供了結果緩沖,Hibernate提供了對Oracle數據庫SQL指令的優化,OJB提供JDO API、OMDB API)
2).數據訪問對象的工廠策略
通過調整抽象工廠和工廠方法模式,DAO模式可以達到很高的靈活度。
當低層存儲不會隨著實現變化而變化時,該策略可以使用工廠方法模式來實現該策略。以產生應用程序需要的大量DAO。圖2是這種情況下的類圖。
此主題相關圖片如下:

當低層存儲隨著實現變化而變化時,該策略可以使用抽象工廠方法模式而實現。
圖3是這種情況下的類圖。
此主題相關圖片如下:

5.結果
1).啟用透明性
業務對象可以是使用數據源,而無須了解該數據源實現的具體細節。訪問是透明的,原因是實現被隱藏在DAO的內部。
2).啟用更容易的遷移
DAO層使應用程序更加容易地遷移到一個不同的數據庫實現。業務對象不了解低層數據實現。因而,該遷移只涉及對DAO層的變化。更進一步說,如果使用工廠策略,則有可能為每一個低層存儲實現提供一個具體工廠實現。在這種情況下,遷移到不同的遷移實現意味著給應用程序提供一個新的工廠實現。
3).減少業務對象中代碼復雜度
由于DAO管理所有的數據訪問復雜性,它可以簡化業務對象和其他使用DAO的客戶端中的代碼。所有與實現有關的代碼(比如sql語句)都被包含在DAO中,而不是包含在業務對象中。這樣做提高了代碼的可讀性,已經代碼生產效率。
4).把所有的數據訪問集中到一個獨立的層。
因為所有的數據訪問操作現在被委托給DAO,所有單獨的數據訪問層可以被看作把數據訪問實現與應用程序中的其他代碼相隔離的。這種集中化使應用程序更容易地維護和管理。
5).不適用于容器管理的持久性
由于EJB容器用容器管理的持久性(CMP)來管理實體bean,該容器會自動地服務所有的持久性存儲訪問。使用容器管理的實體bean的應用程序不需要DAO層,因為該應用程序服務器透明地提供該功能。然而,當需要組合使用CMP和BMP時,DAO仍舊有用處。
6).添加其他層
DAO會在數據客戶端和數據源之間創建其他的對象層,其中該數據源需要被設計和實現以便于權衡該模式的好處。但是選擇本方法也會帶來額外的開銷。
7).需要類層次設計
在使用工廠策略時,我們需要設計和實現具體工廠的層次,以及這些工廠產生的具體產品層次。如果能夠確保這種靈活性,則有必要考慮這種額外的工作。這樣做會增加設計的復雜性。然而,在實現該工廠策略時,你可以首先考慮工廠方法模式,然后再根據需要過渡到抽象工廠。
六.范例代碼
1.實現數據訪問對象模式
范例9-4時表示Customer信息的持久性對象的DAO范例代碼。當findCustomer()被調用時,CloudscapeCustomerDAO創建一個Customer值對象。
范例9-6是使用DAO的范例代碼。
2.實現數據訪問對象的工廠策略
1)使用工廠方法模式
2)使用抽象工廠模式
范例代碼9-2是CloudscapeDAOFactory的范例代碼。
范例代碼9-3中的CustomerDAO接口為Customer持久性對象定義了DAO方法,這些接口是被所有具體DAO實現來實現的,比如CloudscapeCustomerDAO、OracleCustomerDAO、已經SybaseCustomerDAO。Account和OrederDAO接口也與此類似。
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 :
posted on 2007-01-25 17:47 都市淘沙者 閱讀(2624) 評論(1) 編輯 收藏 所屬分類: Java Basic/Lucene/開源資料