Java數(shù)據(jù)對(duì)象(JDO)的前世今生
發(fā)表時(shí)間:2024-01-15 來源:明輝站整理相關(guān)軟件相關(guān)文章人氣:
[摘要]1 Java與數(shù)據(jù)庫(kù)應(yīng)用,JDBC Java發(fā)明以來,在短短的幾年之間,迅速占領(lǐng)了從桌面應(yīng)用(J2SE)到服務(wù)器(J2EE),再到小型設(shè)備嵌入式系統(tǒng)(J2ME)的應(yīng)用開發(fā)市場(chǎng),其語言吸取了SmallTalk的一切皆對(duì)象的理念,擺脫了C++的歷史累贅,簡(jiǎn)潔、自由的風(fēng)格贏得了很多開發(fā)者的喜愛。從J...
1 Java與數(shù)據(jù)庫(kù)應(yīng)用,JDBC
Java發(fā)明以來,在短短的幾年之間,迅速占領(lǐng)了從桌面應(yīng)用(J2SE)到服務(wù)器(J2EE),再到小型設(shè)備嵌入式系統(tǒng)(J2ME)的應(yīng)用開發(fā)市場(chǎng),其語言吸取了SmallTalk的一切皆對(duì)象的理念,擺脫了C++的歷史累贅,簡(jiǎn)潔、自由的風(fēng)格贏得了很多開發(fā)者的喜愛。從JDK1.1開始,Java成為實(shí)用的語言,而不是被人觀望的新品秀;再經(jīng)過JDK1.2的大量增強(qiáng)(尤其是Collection Framework),JDK1.3的虛擬機(jī)效率提升(HotSpot),JDK1.4的融合百家之長(zhǎng)(Logging、RegExp、NewIO等),現(xiàn)在已經(jīng)是成熟穩(wěn)重,頗顯大家風(fēng)范。
在企業(yè)級(jí)市場(chǎng)上,大部分的應(yīng)用建立在數(shù)據(jù)庫(kù)基礎(chǔ)上,數(shù)據(jù)是企業(yè)的生命,傳統(tǒng)開發(fā)語言,包括面向過程的C、面向?qū)ο蟮腃++、變種Pascal的Delphi(非常棒的語言,我用過四年),面向數(shù)據(jù)的PowerBuilder等等,先后在數(shù)據(jù)庫(kù)開發(fā)的舞臺(tái)上展現(xiàn)風(fēng)姿。Java當(dāng)然不會(huì)放過這些,于是,出現(xiàn)了JDBC。在JDBC的幫助下,Java也迅速滲入數(shù)據(jù)庫(kù)開發(fā)的市場(chǎng),尤其是面向企業(yè)服務(wù)器的應(yīng)用開發(fā)。
今天要談的JDO,與JDBC有非常密切的關(guān)系,盡管JDO并不是只面向JDBC的數(shù)據(jù)對(duì)象包裝規(guī)范。下面先簡(jiǎn)單地介紹一下JDBC。
1.1 關(guān)系數(shù)據(jù)庫(kù)之百家爭(zhēng)鳴,ODBC
關(guān)系數(shù)據(jù)庫(kù)的歷史一言難盡,我只能從我的接觸經(jīng)歷和所見所聞,簡(jiǎn)單地?cái)⑹鲆幌隆W钤绲臅r(shí)候,計(jì)算機(jī)還只在一些大型的研究機(jī)關(guān)露面,并不是普羅大眾可以涉及的。蘋果電腦將個(gè)人電腦引入民間,再隨著IBM的PC標(biāo)準(zhǔn)開放,個(gè)人電腦逐步普及開來,加上微軟的DOS操作系統(tǒng),以及Borland的Turbo系列語言開發(fā)環(huán)境,老百姓發(fā)現(xiàn)原來電腦可以做這么多事!后來,出現(xiàn)了DBASE,一個(gè)簡(jiǎn)單的關(guān)系數(shù)據(jù)庫(kù)系統(tǒng),和SQL語言。后來,Borland看到了數(shù)據(jù)庫(kù)的市場(chǎng)前景,推出了Paradox(也是當(dāng)今Delphi和C++Builder中仍然使用的Paradox),一舉占領(lǐng)了民用數(shù)據(jù)庫(kù)的大部分江山,之后,Borland干脆收購(gòu)了Dbase,后來又購(gòu)買了InterBase,將數(shù)據(jù)庫(kù)市場(chǎng)的領(lǐng)先優(yōu)勢(shì)一直保持到Windows3.0出現(xiàn)。這時(shí)候,微軟在Windows1.0和2.0被人痛罵之后頑強(qiáng)地推出3.0,以及更穩(wěn)定的3.1和Win32API,造就了個(gè)人電腦桌面操作系統(tǒng)的霸主地位,在Borland未警覺的情況下,購(gòu)買了同樣具有類Dbase數(shù)據(jù)庫(kù)技術(shù)的Fox公司,并迅速將其易用化,形成了FoxBase,后來演變成FoxPro,逐漸超過了Borland,成為個(gè)人電腦數(shù)據(jù)庫(kù)的大戶。微軟再接再勵(lì),為簡(jiǎn)單易用而低負(fù)荷要求的數(shù)據(jù)庫(kù)應(yīng)用開發(fā)了Access,贏得了廣大開發(fā)人員的心。當(dāng)然,同期的Oracle、Sybase、Informix等商用數(shù)據(jù)庫(kù)憑專注于企業(yè)級(jí)數(shù)據(jù)庫(kù)技術(shù)成為高端的幾位領(lǐng)軍人物。微軟當(dāng)然也想成為高端數(shù)據(jù)庫(kù)供應(yīng)商之一,于是自行開發(fā)一套面向企業(yè)級(jí)應(yīng)用的數(shù)據(jù)庫(kù),不過很快項(xiàng)目夭折,微軟不甘心,購(gòu)買了Sybase的底層TDS技術(shù),包裝成了SQL Server,憑微軟的高度易用性的特點(diǎn),也占領(lǐng)了不少市場(chǎng)。
當(dāng)市場(chǎng)上出現(xiàn)眾多的數(shù)據(jù)庫(kù)產(chǎn)品之后,Borland和微軟都發(fā)現(xiàn)自己擁有的數(shù)據(jù)庫(kù)產(chǎn)品挺多,市場(chǎng)也不小,不同的產(chǎn)品給用戶帶來不同的配置任務(wù),不利于所有產(chǎn)品的推廣,于是,兩者紛紛開始制定數(shù)據(jù)庫(kù)訪問的規(guī)范,微軟推出了ODBC,其面向開發(fā)人員的親和性,逐步獲得了認(rèn)可,同時(shí),Borland糾集了IBM和Novell也推出了IDAPI數(shù)據(jù)庫(kù)接口規(guī)范,也就是今天BDE的核心,不過后來Novell和IBM先后退出,只剩Borland獨(dú)力支撐。不過Borland是一個(gè)技術(shù)實(shí)力雄厚的公司,其技術(shù)一向領(lǐng)先于微軟,BDE的性能比初期的ODBC不知道要好多少倍,后來微軟偷師學(xué)藝,把連接池等技術(shù)加到ODBC中,在Delphi3.0及其BDE在市場(chǎng)上風(fēng)光無限的時(shí)候,逐步趕了上來并有超過。直到今天,BDE仍是Borland的產(chǎn)品線上的數(shù)據(jù)庫(kù)訪問標(biāo)準(zhǔn),而微軟如果不是將ODBC和多數(shù)數(shù)據(jù)庫(kù)的客戶端內(nèi)嵌進(jìn)Windows的話,估計(jì)BDE仍是市場(chǎng)的贏家。不過,微軟是玩弄市場(chǎng)的老手,通過對(duì)操作系統(tǒng)的壟斷,其數(shù)據(jù)庫(kù)產(chǎn)品和ODBC標(biāo)準(zhǔn)終究占據(jù)了多數(shù)開發(fā)市場(chǎng)。
1.2 從optional pack到JDK的標(biāo)準(zhǔn)API
Java開始涉及數(shù)據(jù)庫(kù)應(yīng)用后,Sun就極力制定Java的數(shù)據(jù)庫(kù)規(guī)范,JDBC API就是類似ODBC一樣,對(duì)數(shù)據(jù)庫(kù)訪問的底層協(xié)議進(jìn)行最基本的包裝,然后形成一套統(tǒng)一的數(shù)據(jù)訪問接口,數(shù)據(jù)庫(kù)連接、SQL語句句柄、結(jié)果集,都帶有ODBC的影子。以方便配置為目的,Sun極力推薦完全瘦客戶端的TYPE 4型JDBC驅(qū)動(dòng),這是一個(gè)不需要安裝數(shù)據(jù)庫(kù)客戶端的驅(qū)動(dòng)規(guī)范,是現(xiàn)在使用最多的。當(dāng)然,為了保持與舊的數(shù)據(jù)庫(kù)兼容,JDBC規(guī)范中包括了專用于連接ODBC的TYPE 1驅(qū)動(dòng)和需要安裝數(shù)據(jù)庫(kù)客戶端的TYPE 2驅(qū)動(dòng),以及可以由廠商在數(shù)據(jù)庫(kù)服務(wù)端專門提供面向JDBC的服務(wù)的TYPE 3驅(qū)動(dòng)。
JDBC最早出現(xiàn)時(shí),還不屬于標(biāo)準(zhǔn)JDK的一部分,而是作為一個(gè)額外包提供下載。后來,隨著Java編寫的數(shù)據(jù)庫(kù)應(yīng)用的的增多,和JDBC規(guī)范本身的逐漸成熟,JDBC終于成為JDK1.1的一部分。
JDBC目前最新的是3.0版本,還有正在討論中的4.0版本。實(shí)際上,在開發(fā)中使用得最多的還是1.0中的API,2.0中主要增加了可雙向滾動(dòng)的結(jié)果集、更新批處理等提高可用性和性能的API,3.0主要增加了連接池、可更新的結(jié)果集等特性。4.0將在可管理性、連接池規(guī)范化等方面再做改進(jìn)。
2 面向?qū)ο笈c數(shù)據(jù)庫(kù)
現(xiàn)在的程序員,沒有不知道面向?qū)ο蟮摹W鳛榻咏鎸?shí)客觀世界的開發(fā)概念,面向?qū)ο笫钩绦虼a更易讀、設(shè)計(jì)更合理。在普遍存在的數(shù)據(jù)庫(kù)應(yīng)用領(lǐng)域,開發(fā)人員對(duì)面向?qū)ο蟮淖非髲奈赐V惯^。從八十年代開始,就有很多公司和研究機(jī)構(gòu)在進(jìn)行著面向?qū)ο笈c數(shù)據(jù)庫(kù)結(jié)合的研究。
2.1 SmallTalk、C與C++、Delphi-Object Pascal、Java
面向?qū)ο蟮恼Z言最早有好幾種雛形,IBM的SmallTalk是其中最為流行的,在SmallTalk中,一切都是對(duì)象,一切都是類,它將面向?qū)ο蟮母拍畎l(fā)揮到了極致。面向?qū)ο蟮木幊瘫绕饌鹘y(tǒng)的面向過程的方式挺進(jìn)了一大步,使人們認(rèn)識(shí)到:原來軟件可以這樣寫。不過,由于計(jì)算機(jī)基本結(jié)構(gòu)與底層硬件體系和系統(tǒng)軟件的限制,SmallTalk還不能在理想的性能前提下推廣到普通的應(yīng)用上,這一點(diǎn)暫時(shí)限制了SmallTalk的發(fā)展,接著,C語言的面向?qū)ο蟀鍯++出現(xiàn)了,由于使用C語言的人很多,C++很快成為面向?qū)ο缶幊痰闹髁髡Z言。不過,為了保證與C的兼容,C++保留了很多面向過程的痕跡,比如惡心的指針、全局變量等等。Pascal的改進(jìn)版Object Pascal相對(duì)來說安全許多,后來Borland干脆將Object Pascal換了個(gè)名字,叫Delphi,從此開創(chuàng)了一片面向?qū)ο缶幊痰男率澜纾?Delphi的嚴(yán)謹(jǐn)語法和快速編譯吸引了眾多的應(yīng)用開發(fā)者,加上Borland的完美的VCL組件體系,比起MFC來方便而容易,另外,Delphi完整的數(shù)據(jù)庫(kù)組件,也將數(shù)據(jù)庫(kù)開發(fā)變得簡(jiǎn)單而容易,Delphi再次成為成熟的面向?qū)ο箝_發(fā)語言。微軟當(dāng)然不會(huì)放過這些,通過將MFC內(nèi)置到操作系統(tǒng)中,微軟的VC++也搶回一些市場(chǎng)。這也是為什么Delphi開發(fā)的應(yīng)用程序編譯后會(huì)比VC、VB開發(fā)的程序大的原因。
1995年,Sun的一個(gè)開發(fā)小組本來為了小型嵌入式系統(tǒng)開發(fā)OAK語言,結(jié)果無心插柳柳成蔭,發(fā)展出了Java語言,它是一個(gè)完全擺脫了傳統(tǒng)語言的各種負(fù)擔(dān)的面向?qū)ο蟮恼Z言,當(dāng)然,也保留了一些非面向?qū)ο蟮暮诵模ㄔ碱愋停┮员WC速度,F(xiàn)在Java也為最流行的面向?qū)ο笳Z言之一。當(dāng)然,微軟同樣不會(huì)放過它,擅于模仿的微軟立即弄出一個(gè)C#來與之競(jìng)爭(zhēng),并在C#中保留了一些變種的指針(指代)以吸引傳統(tǒng)的C開發(fā)者。關(guān)于這些語言的各自特點(diǎn),這里就不一一贅述了。
2.2 數(shù)據(jù)庫(kù)與數(shù)據(jù)對(duì)象化
數(shù)據(jù)庫(kù)是企業(yè)級(jí)應(yīng)用不可缺少的,因此,在面向?qū)ο罅餍械臅r(shí)候,數(shù)據(jù)庫(kù)廠商也在進(jìn)行著數(shù)據(jù)對(duì)象化的研究。這些研究在上個(gè)世紀(jì)八十年代就初現(xiàn)端倪。
數(shù)據(jù)庫(kù)的對(duì)象化一般有兩個(gè)方向:一個(gè)是在主流的關(guān)系數(shù)據(jù)庫(kù)的基礎(chǔ)上加入對(duì)象化特征,使之提供面向?qū)ο蟮姆⻊?wù),但訪問語言還是基于SQL;另一個(gè)方向就是徹底拋棄關(guān)系數(shù)據(jù)庫(kù),用全新的面向?qū)ο蟮母拍顏碓O(shè)計(jì)數(shù)據(jù)庫(kù),這就是對(duì)象數(shù)據(jù)庫(kù)ODBMS。
2.2.1 關(guān)系數(shù)據(jù)庫(kù)對(duì)象化、SQL99與JDBC3.0
隨著許多關(guān)系數(shù)據(jù)庫(kù)廠商開始提供對(duì)象化服務(wù),各自的接口開始互不兼容,在經(jīng)歷一些麻煩之后,關(guān)系數(shù)據(jù)庫(kù)廠商感覺到規(guī)范化的必要,因?yàn)楫?dāng)初關(guān)系數(shù)據(jù)庫(kù)雄霸天下時(shí)SQL92標(biāo)準(zhǔn)起了很大作用,大家可以按照統(tǒng)一的編程方式來訪問高性能的商用數(shù)據(jù)庫(kù)。
關(guān)系數(shù)據(jù)庫(kù)廠商集中起來,重新將對(duì)象化服務(wù)規(guī)范起來,形成了SQL99規(guī)范,將其中的對(duì)象結(jié)構(gòu)等內(nèi)容規(guī)范起來,開始一個(gè)嶄新的面向?qū)ο蟮年P(guān)系數(shù)據(jù)庫(kù)(ORDBMS)的歷程。
JDBC3.0就是在這種情況下出臺(tái)的,它將對(duì)關(guān)系數(shù)據(jù)庫(kù)中的對(duì)象服務(wù)的訪問API規(guī)范起來,為Java平臺(tái)提供了訪問ORDBMS的標(biāo)準(zhǔn)方式。當(dāng)然,JDBC3.0對(duì)傳統(tǒng)的SQL操作也進(jìn)行了很多功能增強(qiáng)。
Oracle是一個(gè)傳統(tǒng)的關(guān)系數(shù)據(jù)庫(kù)廠商,在對(duì)象化的道路上,Oracle當(dāng)然采取追加對(duì)象化特征的道路,以侵入數(shù)據(jù)對(duì)象化的市場(chǎng),保持Oracle在數(shù)據(jù)庫(kù)領(lǐng)域的領(lǐng)導(dǎo)地位。如果說Oracle7.4使Oracle走向全盛的話,從Oracle8開始,Oracle就成為關(guān)系數(shù)據(jù)庫(kù)加對(duì)象類型的先驅(qū)。在Oracle8中,我們可以定義一些數(shù)據(jù)結(jié)構(gòu)(Record),將普通的類型包裝在其中成為數(shù)據(jù)元素,然后可以在客戶端按Record結(jié)構(gòu)進(jìn)行訪問,初步提供了面向?qū)ο蟮臄?shù)據(jù)庫(kù)服務(wù)。
2.2.2 對(duì)象數(shù)據(jù)庫(kù)
對(duì)象數(shù)據(jù)庫(kù)就是采用全新的面向?qū)ο蟾拍顏碓O(shè)計(jì)數(shù)據(jù)庫(kù)的全新數(shù)據(jù)庫(kù)類型。在這方面,主要以一些大學(xué)研究機(jī)構(gòu)進(jìn)行設(shè)計(jì)和開發(fā),有些也形成了產(chǎn)品,不過由于市場(chǎng)方面的原因(主要是關(guān)系數(shù)據(jù)庫(kù)的容易上手和市場(chǎng)絕對(duì)領(lǐng)導(dǎo)地位)和ODBMS先天的一些弱點(diǎn)(比如查詢引擎很難優(yōu)化),使ODBMS沒有象關(guān)系數(shù)據(jù)庫(kù)那樣流行起來。
不過對(duì)象數(shù)據(jù)庫(kù)的對(duì)象化特點(diǎn)還是令人割舍不下,目前還是有一些很好的產(chǎn)品在市場(chǎng)上,從商用的到免費(fèi)的都用。目前在ODBMS領(lǐng)域占據(jù)領(lǐng)導(dǎo)地位的是Versant、FastObjects和ObjectStore等幾大廠商,并且,市場(chǎng)份額也在逐步擴(kuò)展。免費(fèi)的產(chǎn)品包括C++編寫的Ozone、純Java的db4o等等。還有一些研究機(jī)構(gòu)開發(fā)一些底層的面向?qū)ο髷?shù)據(jù)庫(kù)引擎,但只提供一些底層的API,不提供管理方面的功能,以及一些算法提供開放式接口,讓廠商去選擇和實(shí)現(xiàn)。比如美國(guó)威斯康新大學(xué)計(jì)算機(jī)系數(shù)據(jù)庫(kù)組的SHORE引擎,就是一個(gè)非常出色的面向?qū)ο髷?shù)據(jù)庫(kù)引擎,現(xiàn)在還在積極的更新中,一些其它研究機(jī)構(gòu)和數(shù)據(jù)庫(kù)廠商采用它完成了自己的特別的對(duì)象數(shù)據(jù)庫(kù),比如專用于地理信息的數(shù)據(jù)庫(kù)、專用于宇宙空間數(shù)據(jù)研究的數(shù)據(jù)庫(kù)等等。
目前對(duì)象數(shù)據(jù)庫(kù)最大的障礙是缺乏統(tǒng)一的規(guī)范,各個(gè)數(shù)據(jù)庫(kù)廠商有各自的訪問接口。對(duì)象數(shù)據(jù)庫(kù)比起關(guān)系數(shù)據(jù)庫(kù)來,不只是基本的幾種數(shù)據(jù)類型那么簡(jiǎn)單,它還涉及繼承處理、多態(tài)等一大堆面向?qū)ο筇卣鞯膶?shí)現(xiàn),規(guī)范化道路當(dāng)然困難重重。這也是對(duì)象數(shù)據(jù)庫(kù)無法普及的一個(gè)重要原因。
也有一些機(jī)構(gòu)提出了一些建議的規(guī)范,比如制定Corba標(biāo)準(zhǔn)的OMG小組的一個(gè)分組ODMG提出的ODMG規(guī)范,目前已經(jīng)是3.0版本,其中的OQL對(duì)象查詢語言相當(dāng)具有吸引力。還有一些中立的機(jī)構(gòu)提出了其它的一些標(biāo)準(zhǔn)化的對(duì)象訪問API,也可算是面向?qū)ο髷?shù)據(jù)庫(kù)的規(guī)范之一。象前面提到的FastObjects和Ozone就是符合ODMG3.0規(guī)范的。
3 Java對(duì)象映射
話說回來,在一般的開發(fā)人員眼中,數(shù)據(jù)庫(kù)就是指關(guān)系數(shù)據(jù)庫(kù),因此,很多應(yīng)用還是采用簡(jiǎn)單的JDBC來訪問數(shù)據(jù)庫(kù)。在開發(fā)的過程中,大家逐漸感覺到JDBC的局限性,比如調(diào)用復(fù)雜、容易產(chǎn)生資源泄漏等等,與面向?qū)ο蟮腏ava語言有一段距離,因此,很多開發(fā)小組開始思考如何將應(yīng)用中的數(shù)據(jù)進(jìn)行對(duì)象化建模,然后再想辦法與JDBC結(jié)合起來,這就是Java數(shù)據(jù)庫(kù)開發(fā)中的層出不窮的對(duì)象包裝技術(shù)。
3.1 對(duì)象包裝技術(shù)
3.1.1 傳統(tǒng)包裝與演變
傳統(tǒng)包裝顧名思義,就是最初出現(xiàn)的包裝方式,很多公司都經(jīng)歷過這一步,產(chǎn)生了很多風(fēng)格各異的包裝方法。當(dāng)然,筆者也有過還算豐富的嘗試過程。
舉例來說,如果我們有一個(gè)用戶類:
public class User {
public int userId;
public String name;
public java.util.Date birthday;
}
我們可以將其當(dāng)作一個(gè)簡(jiǎn)單的數(shù)據(jù)類,然后寫一些工具方法來實(shí)現(xiàn)與JDBC的交互。這些方法,我們可以放到一個(gè)另外的工具類中,也可以放到User類中作為靜態(tài)方法。這些方法包括簡(jiǎn)單的增、刪、改、查(以O(shè)racle為例):
public class User {
public int userId;
public String name;
public java.util.Date birthday;
public static User addUser(String name, Date birthday) throws SQLException {
Connection conn = …; //獲取一個(gè)JDBC連接
PreparedStatement ps = conn.prepareStatement("…"); // 獲取一個(gè)序列值來作為用戶標(biāo)識(shí)
ResultSet rs = ps.executeQuery();
rs.next();
User user = new User();
user.userId = rs.getInt(1); //讀取序列值為新用戶標(biāo)識(shí)
user.name = name;
user.birthday = birthday;
ps = conn.prepareStatement("insert into …."); //插入用戶數(shù)據(jù)記錄的SQL
ps.setInt(1,user.id);
ps.setString(2,user.name);
ps.setDate(3,user.birthday);
ps.executeUpdate();
rs.close();
ps.close();
conn.close();
return user;
}
public static void deleteUser(int userId) throws SQLException {
Connection conn = ….;
//…
}
public static User getById(int userId) throws SQLException {
//…
}
//…
}
以上就是一個(gè)簡(jiǎn)單的數(shù)據(jù)包裝的基本雛形,我們可以看到,這是一個(gè)非常簡(jiǎn)單的JDBC包裝,一些代碼可以模塊化,以實(shí)現(xiàn)重用。另外,這段代碼還有很大隱患,就是中途如果出現(xiàn)異常的話,就會(huì)使系統(tǒng)出現(xiàn)JDBC資源漏洞,因?yàn)镴DBC分配的資源(conn,ps,rs等)是不能被Java虛擬機(jī)的垃圾回收機(jī)制回收的。因此,我們的addUser方法就需要改成下面的樣子:
public static User addUser(String name, Date birthday) throws SQLException {
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
User user = new User();
try {
conn = …; //獲取一個(gè)JDBC連接
ps = conn.prepareStatement("…"); // 獲取一個(gè)序列值來作為用戶標(biāo)識(shí)
rs = ps.executeQuery();
rs.next();
user.userId = rs.getInt(1); //讀取序列值為新用戶標(biāo)識(shí)
user.name = name;
user.birthday = birthday;
ps = conn.prepareStatement("insert into …."); //插入用戶數(shù)據(jù)記錄的SQL
ps.setInt(1,user.id);
ps.setString(2,user.name);
ps.setDate(3,user.birthday);
ps.executeUpdate();
} finally {
//這里注意一定要按照創(chuàng)建的順序關(guān)閉JDBC資源:
if(rs != null) try { rs.close(); } catch(SQLException ex) { ex.printStackTrace(); }
if(ps != null) try { ps.close(); } catch(SQLException ex) { ex.printStackTrace(); }
if(conn != null) try { conn.close(); } catch(SQLException ex) { ex.printStackTrace(); }
}
return user;
}
所有的數(shù)據(jù)庫(kù)訪問方法都必須進(jìn)行這樣的包裝,當(dāng)我們的數(shù)據(jù)類達(dá)到一定的數(shù)量后(比如十幾個(gè),幾十個(gè)),這些方法占據(jù)了大量的代碼,維護(hù)困難、出現(xiàn)BUG機(jī)會(huì)增多,并且不易排錯(cuò),尤其是資源漏洞這種容易引起服務(wù)器穩(wěn)定性問題的BUG。
為了保持?jǐn)?shù)據(jù)類的純潔,我們可以將JDBC操作方法集中到一個(gè)公共工具類中去完成,這樣,這個(gè)工具類會(huì)非常龐大,但每個(gè)數(shù)據(jù)類會(huì)變得很簡(jiǎn)單,這種方式,可以稱作是DataAccessObject模式,相當(dāng)于EJB中的ValueObject,是脫離數(shù)據(jù)庫(kù)細(xì)節(jié)的純對(duì)象模型。
這些都是最基本的存儲(chǔ)處理,在基本增刪改查(這里的查指按關(guān)鍵字查找對(duì)象)的基礎(chǔ)上,我們還需要進(jìn)行復(fù)雜的匹配查詢(SQL),這使得我們的存儲(chǔ)處理代碼進(jìn)一步復(fù)雜化。簡(jiǎn)單地,我們可以寫一個(gè)類似的方法:
public static Collection findBy(String sql) throws SQLException {
//… (這里獲取JDBC連接)
Collection col = new Vector();
ps = conn.prepareStatement(sql);
rs = ps.executeQuery();
while(rs.next()) {
User user = new User();
user.userId = rs.getInt(1);
user.name = rs.getString(2);
user.birthday = rs.getDate(3);
col.add(user);
}
return col;
//… (同前,這里是清理JDBC資源的代碼)
}
這就是一個(gè)查詢接口的基本定義,查詢采用的語言仍是SQL。
如果我們需要將參數(shù)從SQL串中獨(dú)立出來以節(jié)省數(shù)據(jù)庫(kù)的解析時(shí)間并規(guī)范化,我們還需要將查詢條件作為參數(shù)傳遞到這個(gè)方法中去,方法的接口改為:
public static Collection findBy(String sql, Object[] params) throws SQLException {
//…
ps = conn.prepareStatement(sql);
for(int i = 0; i < params.length; i++) ps.setObject(i+1,params[i]);
//…
}
調(diào)用的時(shí)候sql參數(shù)中會(huì)包含一些"?"號(hào),如:
select ID,NAME,BIRTHDAY from USER where … = ? and … > ? and …
當(dāng)然,也有一些開發(fā)團(tuán)隊(duì)喜歡將所有可能的查詢都寫死成一個(gè)個(gè)的專用查詢方法,在其中完成對(duì)應(yīng)的SQL操作,這一點(diǎn)類似于EJBQL,只不過是將EJBQL中容器實(shí)現(xiàn)的功能通過手工編碼來實(shí)現(xiàn)。這樣做使得查詢受到限制,但可以提供更保險(xiǎn)的接口。
還有一些開發(fā)人員看到每個(gè)類中寫這樣一些查詢方法使得這個(gè)類的代碼變得龐大,維護(hù)麻煩,便將所有的查詢方法放到一個(gè)公共的工具類中去,只是在方法中再加入一個(gè)參數(shù):Class cls來表示需要查詢哪個(gè)對(duì)象,使得每個(gè)數(shù)據(jù)類變得緊湊一些。當(dāng)然,這樣的結(jié)果是那個(gè)公共類變得異常龐大,誰維護(hù)誰倒霉,可以說是犧牲一人,幸福團(tuán)隊(duì)。不過如果這個(gè)人心理素質(zhì)不夠好、壓力承受能力不強(qiáng)的話,一些對(duì)數(shù)據(jù)類的改動(dòng)可能會(huì)受到他的阻礙,這時(shí)候就是"一夫當(dāng)關(guān),萬夫莫開"。
現(xiàn)在,我們已經(jīng)實(shí)現(xiàn)了基本對(duì)象的包裝,現(xiàn)在才能開始考慮更多的問題。首先,我們可能會(huì)從規(guī)范化的角度出發(fā),給每一個(gè)屬性加上讀寫訪問器getter/setter,在前面的User類中,可能我們會(huì)將基本屬性部分寫為:
public class User {
private int userId;
private String name;
private Date birthday;
//以下是針對(duì)以上屬性的getter/setter,一般可以用IDE工具生成
public int getUserId() { return userId; }
public void setUserId(int value) { userId = value; }
public String getName() { return name; }
public void setName(String value) { name = value; }
public Date getBirthday() { return birthday; }
public void setBirthday(Date value) { birthday = value};
//…
}
這樣,一個(gè)比較規(guī)范的數(shù)據(jù)類包裝就算完成了。
另外,我們知道,面向?qū)ο蟾拍钪,一個(gè)屬性可以是另一個(gè)對(duì)象,也就是說,對(duì)象之間是存在著引用關(guān)系的,這種關(guān)系還分為一對(duì)一、一對(duì)多、多對(duì)多等幾種情況。從一個(gè)既定對(duì)象出發(fā),其某個(gè)屬性可以是另一個(gè)對(duì)象,也可以是包含另一組對(duì)象的集合。那么,在我們的數(shù)據(jù)包裝里面,當(dāng)然最好也能方便地處理對(duì)象之間的關(guān)系。假定現(xiàn)在我們又有一個(gè)數(shù)據(jù)類Group:
public class Group {
public int grouId;
public String groupName;
public Set users; //set of User
}
這里為了簡(jiǎn)單表明含義,暫不采用getter/setter。
而User對(duì)所屬的Group有一個(gè)引用:
public Group belongTo;
在這里,User.belongTo和Group.users就是一個(gè)一對(duì)多的關(guān)系。
在我們的數(shù)據(jù)類中,如何才能實(shí)現(xiàn)數(shù)據(jù)庫(kù)的存取呢?就算是不考慮Group.users這個(gè)反向關(guān)系,光是User.belongTo就是一件頭疼的事。如果我們?cè)谌〕鲆粋(gè)User對(duì)象時(shí)同時(shí)將其Group對(duì)象也取出來,可以保證不會(huì)在訪問某個(gè)用戶的組時(shí)得到一個(gè)null。不過這樣有幾個(gè)缺點(diǎn):
1. 數(shù)據(jù)類的存取處理(JDBC)變得復(fù)雜,需要執(zhí)行很多SQL讀取才行,有時(shí)候只需要訪問User的基本屬性時(shí)浪費(fèi)時(shí)間和資源;尤其是對(duì)集合型屬性的預(yù)讀取,更加可怕
2. 如果按這個(gè)邏輯,雙向的關(guān)系處理變得危險(xiǎn),很容易陷入死循環(huán),如果要避免,必須在類代碼中加入一些特別的機(jī)制,也是很麻煩的事
3. 如果對(duì)象之間的關(guān)系是更復(fù)雜的情況下,比如三個(gè)、四個(gè)對(duì)象之間互相關(guān)聯(lián),那就是一場(chǎng)噩夢(mèng),對(duì)代碼的編寫和維護(hù)都異常艱難
于是,很多開發(fā)人員自然而然地退后一步,在User類中只保留一個(gè)groupId,并不保存Group對(duì)象,這樣,可以將User.belongTo屬性變成int類型,而另外寫一個(gè)getter方法:
public Group getBelongTo() {
return Group.findById(belongTo);
}
而在Group類中,干脆將users屬性去掉,只保留一個(gè)方法:
public Set getUsers() {
return new HashSet(User.findBy("select … from USER where BELONG_TO=?",new Object[]{ new Integer(groupId) }));
}
也許細(xì)心一點(diǎn)的讀者已經(jīng)看出來了,這里的幾個(gè)方法都沒有將SQLException捕捉,也沒有在方法中聲明,也就是說是有語法錯(cuò)誤的,因?yàn)镾QLException不是一個(gè)RuntimeException。不錯(cuò),確實(shí)是這樣,不過我們這里為了簡(jiǎn)單明了起見,省掉這些處理,以更直接清楚地表達(dá)意思。
這樣,實(shí)際上,我們的對(duì)象關(guān)系包裝已經(jīng)名存實(shí)亡,在類的內(nèi)部有很多用于訪問所引用對(duì)象的復(fù)雜代碼,這些已經(jīng)違背了我們將對(duì)象關(guān)系保持的初衷。有些開發(fā)人員甚至不在類中保留訪問關(guān)系對(duì)象的方法,而是在客戶調(diào)用時(shí)再去訪問另一對(duì)象類的讀取方法,如:
…
User user = User.getById(…);
//對(duì)user對(duì)象進(jìn)行一些訪問,如顯示其姓名等等
Group group = Group.findById(user.belongTo);
//對(duì)group對(duì)象進(jìn)行一些訪問,如顯示組名等等
…
在這樣的代碼里,實(shí)際上我們已經(jīng)從根本上退回了關(guān)系數(shù)據(jù)庫(kù)的出發(fā)點(diǎn),這與直接訪問數(shù)據(jù)表有多大區(qū)別呢?只不過是在表上面套了一層貌似面向?qū)ο蟮钠ざ选2恍业氖,這種方式還存在于很多應(yīng)用之中。
從前面提到的這些面向?qū)ο蟀b的細(xì)節(jié)問題,我們可以看到這種傳統(tǒng)包裝方式的一些主要的缺陷:
3.1.1.1 數(shù)據(jù)庫(kù)命名與對(duì)象設(shè)計(jì)命名的一致性問題
很多時(shí)候,我們興致勃勃地設(shè)計(jì)好一個(gè)類圖,并且經(jīng)過評(píng)審之后,開始對(duì)它進(jìn)行數(shù)據(jù)庫(kù)包裝,這時(shí)候,發(fā)現(xiàn)有些屬性名在具體的數(shù)據(jù)庫(kù)中與該數(shù)據(jù)庫(kù)的關(guān)鍵字沖突,不能用作表名或字段名,必須在數(shù)據(jù)表設(shè)計(jì)時(shí)采用另外的名稱,或者很麻煩地加上引號(hào)來使用。于是,寫數(shù)據(jù)庫(kù)處理代碼的人有意見了,他必須很清楚地記得一個(gè)屬性在類中是什么屬性名,在表中又是什么字段名,在編寫的類逐漸增多后,尤其是一個(gè)類經(jīng)過歷次變動(dòng)之后,或者經(jīng)過很長(zhǎng)時(shí)間又需要改動(dòng),并去處理這些JDBC代碼時(shí),代碼維護(hù)人員簡(jiǎn)單要發(fā)瘋了!
3.1.1.2 對(duì)象的查詢?nèi)跃窒抻赟QL
這一點(diǎn)也是這種簡(jiǎn)單的包裝方法最不能擺脫關(guān)系數(shù)據(jù)庫(kù)的地方。上面也已經(jīng)說過,一些命名沖突帶來了很多維護(hù)工作量,代碼中必須還保留數(shù)據(jù)表的命名體系,而不是對(duì)象類的命名體系。這將使調(diào)用人員仍需要清楚記得兩套命名體系,無論時(shí)間經(jīng)過多久,或者開發(fā)人員是否有流動(dòng)。
此外,對(duì)于普遍需要用到的連表查詢,也給應(yīng)用開發(fā)帶來困難,這些地方仍不能突破SQL的限制。
3.1.1.3 SQL資源占用多
在處理對(duì)象集合訪問或者處理集合類型屬性時(shí),往往我們只能在同一個(gè)Connection中處理一切事務(wù),這就要求一次性地將集合中的對(duì)象全部讀入,如果集合很大的話,容易造成數(shù)據(jù)庫(kù)擁塞,使得性能大打折扣,適得其反。這方面的優(yōu)化也是很多開發(fā)人員一直在努力改進(jìn)的。
3.1.1.4 對(duì)象關(guān)系處理復(fù)雜
前面提過,對(duì)象之間的關(guān)系處理上,普通的包裝技術(shù)是一種表面上的處理,在訪問時(shí)調(diào)用者仍需要用大量的代碼進(jìn)行處理,并且,這還只是讀取,在寫入關(guān)系屬性時(shí)會(huì)有更多的細(xì)節(jié)問題需要處理。
3.1.1.5 面向?qū)ο筇厣荒軕?yīng)用一小部分
面向?qū)ο蟮陌b到前面所說的為止,都還只是很基本的處理,而面向?qū)ο蟮木A:繼承和多態(tài),在這里得不到任何幫助。我們放棄了很多合理的設(shè)計(jì)來遷就數(shù)據(jù)庫(kù)的包裝。
以上就是基本數(shù)據(jù)包裝的主要缺陷,還有更多的小問題也需要額外的處理。
不過,有責(zé)任心的開發(fā)人員當(dāng)然不會(huì)就此善罷甘休,他們冥思苦想,可能會(huì)用更好的方式來實(shí)現(xiàn)對(duì)象之間關(guān)系的處理,而且還會(huì)加入一些延遲訪問的機(jī)制,將對(duì)象之間的引用在需要用的時(shí)候才去連接數(shù)據(jù)庫(kù)取數(shù)據(jù),另外,在類的繼承上,他們也試圖去在數(shù)據(jù)庫(kù)包裝上得到體現(xiàn)。
值得感謝的是,一些富有經(jīng)驗(yàn)的團(tuán)隊(duì)在經(jīng)歷若干改進(jìn)之后,毫不吝惜將他們的成果貢獻(xiàn)出來,并將其獨(dú)立化,成為可復(fù)用的組件。這也就是后來出現(xiàn)的對(duì)象包裝產(chǎn)品,包括商用的和免費(fèi)的。
3.1.2 產(chǎn)品化包裝中間件
面向?qū)ο蟮陌b產(chǎn)品化后,逐步推廣開來,稱作關(guān)系/對(duì)象映射:O/R Mapping。并且,在產(chǎn)品化的過程中,產(chǎn)品提供者在產(chǎn)品中逐漸提供了更多的功能。先是一些自動(dòng)命名轉(zhuǎn)換器得以應(yīng)用,將客戶代碼中的類名、屬性名在內(nèi)部處理的時(shí)候自動(dòng)地轉(zhuǎn)換到對(duì)應(yīng)的數(shù)據(jù)庫(kù)結(jié)構(gòu)命名空間上,這樣,應(yīng)用開發(fā)的時(shí)候不再關(guān)心具體的數(shù)據(jù)庫(kù)結(jié)構(gòu),不再需要記憶不同地命名體系,只需要一心一意地根據(jù)類圖來進(jìn)行開發(fā)。在此之后,這些O/R Mapping產(chǎn)品的開發(fā)團(tuán)隊(duì)又向前邁了一大步:引入了詞法分析器,提供面向?qū)ο蟮牟樵冋Z言!很多對(duì)象關(guān)系的查詢使應(yīng)用開發(fā)變得非常方便,使O/R Mapping產(chǎn)品上了一個(gè)新的臺(tái)階。
3.1.2.1 TopLink
TopLink是一個(gè)非常早期的產(chǎn)品,最初面向C++,后來也實(shí)現(xiàn)了Java的映射。TopLink性能優(yōu)異,功能強(qiáng)大,并且提供了獨(dú)特的查詢過濾器機(jī)制,以及對(duì)關(guān)系的處理和查詢都非常有效,于是,TopLink逐漸從商用化O/R Mapping產(chǎn)品中勝出,成為市場(chǎng)上的最出色的映射產(chǎn)品。也正因?yàn)檫@一點(diǎn),最大的關(guān)系數(shù)據(jù)庫(kù)廠商Oracle將其收購(gòu),成為提供最強(qiáng)數(shù)據(jù)庫(kù)和最強(qiáng)對(duì)象映射中間件的廠商。
3.1.2.2 Castor、Hibernate
TopLink雖然強(qiáng)大,但太強(qiáng)大的東西免不了得意忘形,TopLink開始將用戶鎖死到自己的產(chǎn)品上,查詢方式是最突出的。它的查詢體系含有很多別扭的概念(在我看來是如此),但為達(dá)到一般O/R產(chǎn)品不能達(dá)到的功能,開發(fā)者只能接受這些。慢慢地,也產(chǎn)生積怨,再加上其高昂的價(jià)格,讓很多新老用戶望而卻步。于是,免費(fèi)的產(chǎn)品開始崛起。
免費(fèi)的O/R Mapping工具有很多種,這里只提其中最有影響力的兩種:Castor和Hibernate。
Castor是Exolab組織開發(fā)的面向Java的包裝工具,它最大的特色就是實(shí)現(xiàn)了大部分的ODMG OQL規(guī)范,在查詢上,可以象完全使用一個(gè)對(duì)象數(shù)據(jù)庫(kù)一樣類圖進(jìn)行查詢(后面會(huì)有介紹)。它的原理是通過Java反射API去實(shí)現(xiàn)屬性的設(shè)置和讀取。不過由于各種原因,Castor后來的版本更新越來越慢,最終停步在1.0之前,成為至今未出到1.0正式版的O/R Mapping產(chǎn)品。不管怎么樣,它還是一個(gè)相當(dāng)不錯(cuò)的產(chǎn)品。
Hibernate是一個(gè)現(xiàn)在很火熱的O/R Mapping產(chǎn)品,目前已經(jīng)出到2.0版,它功能一樣強(qiáng)大,同樣使用Java反射API進(jìn)行對(duì)象的設(shè)置,但它的查詢語言就是一套比較獨(dú)特的體系,這一點(diǎn)有點(diǎn)類似TopLink,但Hibernate更具有親和力,對(duì)關(guān)系的查詢更方便,只不過比起Castor來,在方便性和規(guī)范性上還是稍遜一籌。就目前狀況而言,Hibernate的用戶量和技術(shù)支持要強(qiáng)一些。
3.2 面向?qū)ο蟮臄?shù)據(jù)庫(kù)查詢
在對(duì)數(shù)據(jù)庫(kù)進(jìn)行面向?qū)ο笱芯康倪^程中,軟件世界的開發(fā)人員和設(shè)計(jì)人員們發(fā)現(xiàn):對(duì)數(shù)據(jù)庫(kù)能夠進(jìn)行對(duì)象化的查詢,才是對(duì)數(shù)據(jù)庫(kù)進(jìn)行徹底的面向?qū)ο蠡_@體現(xiàn)在我們使用一種全新的數(shù)據(jù)庫(kù)查詢語言,能夠很簡(jiǎn)潔易懂地對(duì)數(shù)據(jù)庫(kù)中的對(duì)象進(jìn)行查詢。一個(gè)典型的例子如下:
假設(shè)我們已經(jīng)有前面提到的兩個(gè)數(shù)據(jù)類:User和Group,它們之間有一對(duì)多的關(guān)系:User.belongTo和Group.users。在數(shù)據(jù)庫(kù)中已經(jīng)存在很多這兩個(gè)類的實(shí)例,以及相互之間的關(guān)系。我們可以使用下面的對(duì)象式查詢語言來查詢符合條件的User對(duì)象:
select * from User where User.belongTo.name='GROUP1'
或者
select userId,name from User where User.belongTo.name='GROUP2'
等等。從中我們可以看出,通過使用面向?qū)ο笾械某蓡T屬性指定符".",可以讓我們達(dá)到SQL中的連表的效果,實(shí)際上,第一個(gè)句查詢的SQL等價(jià)版本是:
select a.* from USER a, GROUP b
where a.BELONG_TO = b.GROUP_ID
and b.NAME = 'GROUP1'
由此可見,對(duì)象式的查詢語言,比起實(shí)現(xiàn)同樣功能的SQL語言來說,簡(jiǎn)單了很多,意義也更明確,更符合使用者的思維習(xí)慣。在類圖比較復(fù)雜、查詢涉及的類又比較多的時(shí)候,這種新型的查詢語言體現(xiàn)出絕對(duì)的優(yōu)勢(shì)。
3.2.1 ODMG,OQL,Java Binding
在面向?qū)ο笫讲樵冋Z言的研究過程中,開發(fā)人員們逐漸實(shí)現(xiàn)了相似的查詢語言,然后互想取長(zhǎng)補(bǔ)短,最終在ODMG組織(www.odmg.org)的統(tǒng)一下,形成了規(guī)范化的語言:ODMG OQL,這是一種完全面向?qū)ο蟮臄?shù)據(jù)庫(kù)查詢語言,語法與前面提到的類似,不過考慮了更廣泛的情況,語句更加簡(jiǎn)潔而嚴(yán)謹(jǐn)。
OQL并不是針對(duì)某種語言的,它可以被應(yīng)用到很多種開發(fā)語言中,它不象SQL那樣只是純字符串式的查詢語句,因?yàn)槊嫦驅(qū)ο蟛樵冎羞必須提供相關(guān)類的信息,所以O(shè)QL需要在編程語言中實(shí)現(xiàn)一些特定的API。
目前,ODMG的OQL已經(jīng)被規(guī)范化地應(yīng)用到SmallTalk、Java、C++這些面向?qū)ο蟮某绦蛟O(shè)計(jì)語言當(dāng)中,在ODMG的規(guī)范中,這幾個(gè)模塊被稱作SmallTalk Binding、Java Binding和C++ Binding。
不過,由于歷史原因,ODMG并沒有象想象中地那樣得到廣泛應(yīng)用,現(xiàn)有的十幾個(gè)面向?qū)ο髷?shù)據(jù)庫(kù)中,采用ODMG OQL規(guī)范的少之又少,目前也只有FastObjeccts、Ozone這些產(chǎn)品采納了這個(gè)規(guī)范,而象Versant這樣的大廠商還沒有采取OQL來查詢數(shù)據(jù)庫(kù),而是自己定義了自己的一套API,稱作VQL(Versant Query Lanaguage)。
在JDO之前的O/R Mapping產(chǎn)品中,也有一些產(chǎn)品使用OQL(有時(shí)候是其子集),比如Castor、Apache的Jakarta小組開發(fā)的OJB等等。
3.2.2 第三方協(xié)議
軟件世界是一個(gè)多姿多彩的世界,總有那么一些好事之士不斷地冒出新奇的想法。還有一些開發(fā)面向?qū)ο髷?shù)據(jù)庫(kù)的組織制定了自己的一套對(duì)象式數(shù)據(jù)庫(kù)查詢語言,自己的規(guī)范。
不過這些規(guī)范相對(duì)來說,影響力小得多。比起ODMG來,可以說應(yīng)用范圍太小,更不用說與SQL這樣廣泛應(yīng)用的標(biāo)準(zhǔn)進(jìn)行比較了。
3.2.3 EJBQL
Sun為了使Java應(yīng)用在企業(yè)級(jí)數(shù)據(jù)庫(kù)應(yīng)用中,不遺余力地推廣J2EE,在2001年的時(shí)候,推出了EJB2.0規(guī)范,其中包含了富有特色的面向CMP方式的EntityBean的查詢語言:EJBQL,功能類似于ODMG OQL,只不過只能在EJB發(fā)布時(shí)靜態(tài)地存在于應(yīng)用描述符中,不能在程序中動(dòng)態(tài)地使用。這是EJBQL最大的弱點(diǎn),也許EJB3.0規(guī)范會(huì)將其動(dòng)態(tài)化,但到了那一天,世界多半已經(jīng)不是現(xiàn)在的樣子了。
3.2.4 JDO
JDO中有最近規(guī)定的一個(gè)對(duì)象式查詢語言規(guī)范,稱作JDOQL,比起OQL來,JDOQL將查詢語言中的很多元素與Java語言緊密地結(jié)合在一起,有的人覺得麻煩,有些人覺得規(guī)范,評(píng)論各不相同。從筆者個(gè)人的角度來看,這樣有利于沒寫過數(shù)據(jù)庫(kù)應(yīng)用、沒用過SQL的新手很快地習(xí)慣JDOQL,但實(shí)際上,有多少人會(huì)在沒寫過SQL,沒了解過關(guān)系數(shù)據(jù)庫(kù)的情況下去用JDO寫數(shù)據(jù)庫(kù)應(yīng)用呢?畢竟市場(chǎng)說明了一切。個(gè)人認(rèn)為,JDO中對(duì)數(shù)據(jù)庫(kù)對(duì)象的查詢多少顯得有些累贅,如果能更簡(jiǎn)化一點(diǎn),那將更吸引使用傳統(tǒng)SQL的開發(fā)人員。
4 JDO歷程與主要產(chǎn)品
說起JDO,其來由還有一段特殊的背景。Java語言在JDK1.1達(dá)到比較實(shí)用的目的后,企業(yè)級(jí)數(shù)據(jù)庫(kù)應(yīng)用也正是軟件開發(fā)市場(chǎng)中的重要組成部分,Sun看到這一點(diǎn)后,也希望通過Java這個(gè)強(qiáng)大的武器在數(shù)據(jù)庫(kù)開發(fā)市場(chǎng)攻占市場(chǎng)份額。JDK1.2推出后,Sun同時(shí)推出了面向企業(yè)應(yīng)用的EJB,對(duì)基于java的中間件服務(wù)器框架進(jìn)行了規(guī)范化定義,這就是J2EE。不過在JDK1.2時(shí),Java的速度還是不能與傳統(tǒng)的C/C++和Delphi這樣一些應(yīng)用開發(fā)語言相比。為了防止業(yè)界對(duì)Java的激情因此而消退,Sun宣布將在JDK中加入強(qiáng)大的虛擬機(jī)技術(shù)HotSpot,其中包含更先進(jìn)的垃圾收集算法和更優(yōu)化的Java字節(jié)代碼再編譯技術(shù)(相當(dāng)于JIT,Java即時(shí)編譯技術(shù))。HotSpot引起了Java關(guān)注者的極大興趣,但Sun的HotSpot一拖再拖,最后包含在JDK1.3中出來時(shí),性能也沒有象預(yù)期的那樣好,比起C++編譯生成的代碼來還是有一段距離。
這個(gè)時(shí)候,大家開始對(duì)Sun心存懷疑,而Sun深知這一點(diǎn),于是將公眾的注意力趕緊轉(zhuǎn)移到EJB上,從EJB1.0到EJB1.1,再到EJB2.0,業(yè)界又開始關(guān)注J2EE中間件技術(shù)上來。很快,開發(fā)人同發(fā)現(xiàn)用EJB來編寫數(shù)據(jù)庫(kù)應(yīng)用還是有很大的難度,雖然在分布式技術(shù)上EJB確實(shí)有很大的價(jià)值。在這個(gè)時(shí)候,Sun決定推出JDO技術(shù)作為輕量級(jí)的Java數(shù)據(jù)庫(kù)訪問規(guī)范,而這一點(diǎn)也受到很多傳統(tǒng)O/R Mapping市場(chǎng)的歡迎。為了與傳統(tǒng)O/R Mapping有所區(qū)別,Sun一開始就高姿態(tài)地將JDO定位成不只是面向關(guān)系數(shù)據(jù)庫(kù)的Java規(guī)范,而是針對(duì)所有的存儲(chǔ)技術(shù),包括面向?qū)ο髷?shù)據(jù)庫(kù)和其它類型的存儲(chǔ)體系(如文件),就象EJB EntityBean一樣,雖然。就筆者的角度,這個(gè)做法使第一版的JDO拋棄了很多傳統(tǒng)O/R Mapping提供的面向關(guān)系數(shù)據(jù)庫(kù)的功能,可以算是一個(gè)失策。
4.1 規(guī)范提出、JSR
JDO最早是由Sun召集眾多的O/R Mapping開發(fā)團(tuán)隊(duì)集中起來共同提出的,首先是通過會(huì)議確定了JDO需要包括的內(nèi)容,然后正式提出一個(gè)Java規(guī)范請(qǐng)求(JSR-12),正式開始了JDO規(guī)范的制定。下面是主要的進(jìn)展里程碑。
JSR #000012 approved in July 1999
1999-8組建的專家小組:包括Sun、Apple、BEA、IBM、Oracle、SAP、WebGain等
2000-5 完成公開評(píng)論草案
2000-6 在JavaOne上引入
2001-3 最終草案0.93
2001-5 最終草案0.96公布
2001-6 在JavaOne上啟動(dòng)
2001-11 最終草案0.98
2002-4 1.0版正式公布
2002-8 1.0.1修正版
2003-8 2.0規(guī)范啟動(dòng)
…
4.2 Oracle與JDO
作為JDO專家組的重要成員,同時(shí)作為最大的關(guān)系數(shù)據(jù)庫(kù)廠商的Oracle地位顯然非同一般。在JDO規(guī)范中,Oracle也可說是立下汗馬功勞,很多API的形成,Oracle都提供了很重要的參考意見,最終的投票O(jiān)racle也是毫不猶豫。
可是,世間的事總是變化莫測(cè)的,就在JDO1.0快出臺(tái)之時(shí),Oracle收購(gòu)了TopLink,這一點(diǎn)使Oracle的身份變得特殊而復(fù)雜。TopLink是一個(gè)商業(yè)產(chǎn)品,是以商業(yè)利益為目標(biāo)的,而Oracle也是追求利益最大化的典型商家,這一點(diǎn)與JDO的開放精神背道而馳。因此,我們看到后期Oracle對(duì)JDO的不積極態(tài)度,甚至在前一陣的JavaOne大會(huì)上有人從Oracle的角度非正式地攻擊JDO。
4.3 主要產(chǎn)品以及各自特點(diǎn)
在JDO規(guī)范制定的同時(shí),出現(xiàn)了幾個(gè)主要的JDO產(chǎn)品,有美國(guó)的基于對(duì)象數(shù)據(jù)庫(kù)的FastObjects j1、法國(guó)的支持Versant對(duì)象數(shù)據(jù)庫(kù)、文件數(shù)據(jù)庫(kù)、主流RDBMS的LiDO、南非的JDOGenie、德國(guó)的JRelay等等,這些都是很不錯(cuò)的JDO產(chǎn)品。下面列舉一下我對(duì)主要的幾個(gè)產(chǎn)品的印象:
LiDO(法國(guó)LibeLis公司)
我對(duì)JDO的認(rèn)識(shí)主要是通過LiDO這個(gè)產(chǎn)品,它在2002年3月的一份圖文并茂的教程中簡(jiǎn)要解說了JDO的使用和優(yōu)點(diǎn)。這個(gè)教程可以在這里下載:http://www.objectweb.org/conference/JDO.pdf。LiDO的特色是大而全,支持文件型數(shù)據(jù)庫(kù)、RDBMS、ODBMS,甚至是XML數(shù)據(jù)庫(kù)。不過配置較麻煩。最新版本是2.0RC。
KodoJDO(美國(guó)SolarMetrics公司)
Kodo是JDO的中流砥柱之一,在JDO1.0還未最后通過的時(shí)候,它就是一個(gè)比較成熟的產(chǎn)品了,其特點(diǎn)是注重性能和穩(wěn)定性,目前最新版本是2.5.0,是客戶最多的產(chǎn)品。
JDOGenie(南非HemSphere公司)
這是目前我最推薦的產(chǎn)品,最新版本是1.4.7,性能也不錯(cuò),穩(wěn)定性還有待驗(yàn)證,但它有一個(gè)最大的特點(diǎn):集成性好,最易學(xué),其公司的CTO David Tinker也是一個(gè)善解人意的年輕人,采納了很多網(wǎng)友的意見對(duì)產(chǎn)品進(jìn)行改進(jìn),主要是在配置上非常方便,有一個(gè)專門的圖形界面工具,可以進(jìn)行配置、數(shù)據(jù)庫(kù)生成、對(duì)象查詢等等很實(shí)用的功能。
JRelay(德國(guó)ObjectIndustries公司)
這也是一個(gè)出現(xiàn)得比較早的產(chǎn)品,也有一個(gè)GUI工具用于配置,曾幾何時(shí),這個(gè)工具還是相對(duì)很方便的,但一年多過去了,好象沒什么進(jìn)展,最新版本是2.0,我試過一段時(shí)間,后來就沒有再跟進(jìn)了。
FrontierSuite for JDO (美國(guó)ObjectFrontier)
這個(gè)產(chǎn)品與JRelay、Kodo一起,可算是早期的JDO三個(gè)主要產(chǎn)品,它支持正向開發(fā)和反向開發(fā)(Reverse Engineer)。它的特色是反向工程(從表結(jié)構(gòu)生成數(shù)據(jù)類)比較方便,與UML的結(jié)合也很強(qiáng),不過真正運(yùn)行起來的時(shí)候,配置復(fù)雜。
TJDO(一群跨國(guó)界的有志之士)
這是一個(gè)在Sun提供的參考產(chǎn)品(Reference Implementation)的基礎(chǔ)上加入一些擴(kuò)展功能而形成的一個(gè)免費(fèi)產(chǎn)品,目前最新版本是2.0beta3,不過進(jìn)展也緩慢,這個(gè)版本已經(jīng)出現(xiàn)好幾個(gè)月了沒有進(jìn)一步的更新。
5 目前狀況與未來展望
從2002年4月JDO1.0規(guī)范正式公布以來,各個(gè)產(chǎn)品層出不窮,從商業(yè)到免費(fèi)的層次,都有不錯(cuò)的產(chǎn)品推出。象Kodo、Lido、JDOGenie等產(chǎn)品都已經(jīng)比較成熟,可以考慮投入開發(fā)使用。在2002年8月,JDO又推出1.0.1修正版,修正了1.0版規(guī)范中的一些文字錯(cuò)誤,以及輕微地改進(jìn)了部分異常定義,不過改動(dòng)都不大。從現(xiàn)在的情形來看,除了Kodo有一些大學(xué)的項(xiàng)目用到外,好象還沒看到多少使用JDO作開發(fā)的應(yīng)用。
JDO畢竟是一個(gè)新技術(shù),從概念上到實(shí)際應(yīng)用上對(duì)其掌握的用戶還不多,而這些產(chǎn)品在配置、使用上的方便性易用性還有待大幅度改進(jìn),因此,真正用JDO來開發(fā)項(xiàng)目的用戶還廖廖無幾,至少我還不知道有哪些項(xiàng)目使用了JDO。我自己也嘗試使用JDO來開發(fā)項(xiàng)目,但由于一些JDO1.0版本中還不夠完善的一些硬傷(比如不支持關(guān)系數(shù)據(jù)庫(kù)統(tǒng)計(jì)功能),使我對(duì)JDO仍處于觀望階段。
在八月中旬進(jìn)行的JDO2.0規(guī)劃會(huì)議中,來自各國(guó)的各個(gè)JDO產(chǎn)品廠商、JDO技術(shù)咨詢公司、JDO研究機(jī)構(gòu)的代表匯聚一堂,將各自收集到的用戶對(duì)JDO2.0的需求總結(jié)起來,提出一個(gè)個(gè)的新的議題,并且確定了每個(gè)議題的JDO規(guī)范撰寫負(fù)責(zé)人,比如高級(jí)fetchGroup特性由目前實(shí)現(xiàn)得最好的JDOGenie的CTO David Tinker負(fù)責(zé),關(guān)于managed-relationship特性由Kodo的產(chǎn)品總監(jiān)負(fù)責(zé),用戶要求最多的JDO對(duì)象與PersistenceManager的脫鉤/重掛鉤特性由Sun的Craig Russell親自操刀,等等。
最具有吸引力的JDO2.0議題,筆者個(gè)人認(rèn)為是專門為關(guān)系數(shù)據(jù)庫(kù)設(shè)立的子規(guī)范JDO/R,這也是我一直以來最關(guān)心的,這將使目前JDBC的開發(fā)將可以被JDO完全取代,并且保證開發(fā)過程保持面向?qū)ο蟮奶厣。還有一些將一個(gè)類映射到多個(gè)表之類的特性也在規(guī)范化的列表上,這將有利于DBA在不影響應(yīng)用開發(fā)的前提下根據(jù)需要更改數(shù)據(jù)表結(jié)構(gòu),實(shí)現(xiàn)更好的性能。類似的新特性還有很多,粗略地看,這些都規(guī)范化起來后,真不知道各個(gè)廠商還能做什么樣的擴(kuò)展特性,也許以后廠商之間拼的只能是技術(shù)支持服務(wù)和產(chǎn)品性能了,當(dāng)然,最后還有價(jià)格的競(jìng)爭(zhēng)。
說了這么多,我想大家關(guān)心的還是JDO2.0到底什么時(shí)候能出來,我們什么時(shí)候可以用上它,什么時(shí)候有中文版產(chǎn)品,價(jià)格到底如何。這些問題目前筆者還無法一一回答,只能根據(jù)筆者所掌握的信息初步解釋一下。據(jù)前幾天的JDO2.0啟動(dòng)大會(huì)上David Jordan的預(yù)期,JDO2.0正式版將在18個(gè)月后正式完成。那正式完成后廠商什么時(shí)候才能提供符合規(guī)范的產(chǎn)品呢?這個(gè)問題不用擔(dān)心,因?yàn)橐?guī)范在制定的過程中會(huì)不斷地公布公眾預(yù)覽版(Public Review),這樣,廠商可以先實(shí)現(xiàn)其中的功能,等規(guī)范正式完成后,也不會(huì)有太大的變化,廠商也不會(huì)需要太多時(shí)間來跟進(jìn)最終規(guī)范。所以,我估計(jì)一年之后,我們就可以在JDO2.0上進(jìn)行開發(fā)了。至于價(jià)格如何,1.0規(guī)范的產(chǎn)品初步印象是每個(gè)開發(fā)人員需要一個(gè)License,一個(gè)License大概2000美元。如果JDO2.0產(chǎn)品的價(jià)格還保持這么貴的話(至少對(duì)中國(guó)用戶來說),我們也還有很多免費(fèi)(如JPOX,TJDO)或半免費(fèi)(如JCredo)產(chǎn)品可以選擇。最后一個(gè)關(guān)于中文版的問題,有待于廠商對(duì)中國(guó)市場(chǎng)的考察,或者看看是否有國(guó)內(nèi)的JDO產(chǎn)品了。不過,在JDO2.0規(guī)范制定的同時(shí),我們可以先集眾人之力將其普及,使廣大的使用Java語言進(jìn)行數(shù)據(jù)庫(kù)開發(fā)的開發(fā)人員都來認(rèn)識(shí)JDO,了解JDO,甚至將其用到項(xiàng)目開發(fā)之中。
也許,JDO的目標(biāo)已經(jīng)吸引了Java世界以外的人,微軟發(fā)現(xiàn)了這一點(diǎn),也立即計(jì)劃在.NET體系中加入一個(gè)仿照J(rèn)DO的中間件,具體是采用ObjectStore的產(chǎn)品,ObjectStore是一個(gè)同時(shí)做JDO和.NET-DO(姑且使用這個(gè)名稱)的公司。
在這里,筆者可以大膽地預(yù)測(cè),在未來的一兩年內(nèi),JDO將在Java世界大放光彩!
5.1 一點(diǎn)花絮
在2003年6月舉行的JavaOne大會(huì)上,JDO備受矚目,很多開發(fā)者或開發(fā)組織對(duì)其產(chǎn)生了極大的興趣,在各大網(wǎng)站媒體上也頻頻曝光,大多對(duì)其評(píng)價(jià)不錯(cuò),當(dāng)然,也有一些負(fù)面的評(píng)價(jià)。
不過,網(wǎng)站上的報(bào)道也不可盡信,比如服務(wù)器領(lǐng)域的權(quán)威網(wǎng)站TheServerSide.com上介紹JavaOne大會(huì)第二天關(guān)于JDO的一次會(huì)議的報(bào)道就有誤導(dǎo)之嫌,從報(bào)道的內(nèi)容來看,好象會(huì)議用來答疑與交流的的最后五分鐘全被一個(gè)好事者占據(jù),這個(gè)人拼命鼓吹Oracle的TopLink解決方案和一些易用性的改進(jìn),從而攻擊JDO的市場(chǎng)前景。后來,我就這一信息與參加會(huì)議的JDO專家組的David Jordan交流時(shí),他糾正了我的錯(cuò)誤理解,實(shí)際情形是:那個(gè)好事者是一個(gè)積極的JDO擁護(hù)者,他花了超過五分鐘時(shí)間向大家揭露Oracle的TopLink改進(jìn)實(shí)際上也是抄襲JDO的概念而進(jìn)行的一些API改動(dòng)!不過有一點(diǎn)是相同的,這個(gè)好事者占據(jù)了大家的提問時(shí)間,使這次會(huì)議在意猶未盡中結(jié)束。