我們來考慮應(yīng)用程序必須執(zhí)行的一個(gè)簡單的業(yè)務(wù)過程。生成訂單發(fā)票需要完成以下工作:
1.確定適當(dāng)?shù)牟樵儯瓚?yīng)用程序查詢ORDERS和LINE_ITEMSGE表搜集生成訂單發(fā)票所需要的全部信息。可能需要涉及兩個(gè)表的聯(lián)接操作,也可能要求每次涉及一個(gè)表的多次查詢。
2.發(fā)出查詢--如果沒有可用的連接,應(yīng)用程序初始化一個(gè)數(shù)據(jù)庫連接,并直接與數(shù)據(jù)庫驅(qū)動(dòng)程序交互,發(fā)出查詢。
3.解析查詢結(jié)果--應(yīng)用程序讀取查詢結(jié)果中的數(shù)據(jù),創(chuàng)建Order和LineItem對象表示這些數(shù)據(jù)。可能還涉及把數(shù)據(jù)從數(shù)據(jù)庫格式轉(zhuǎn)換成更符合應(yīng)用程序語義的格式。
4.打印發(fā)票--應(yīng)用程序使用Order和LineItem遍歷所在的訂單,計(jì)算總價(jià)并打印發(fā)票。
第一步需要應(yīng)用程序和數(shù)據(jù)模型緊密耦合,國為應(yīng)用程序需要構(gòu)造查詢。類似地,第二步和第三步耦合了應(yīng)用程序和數(shù)據(jù)訪問代碼,在這里就是數(shù)據(jù)庫驅(qū)動(dòng)程序及其資源。
對象/關(guān)系映射模式同時(shí)去掉了數(shù)據(jù)模型和數(shù)據(jù)訪問與應(yīng)用程序及其域?qū)ο蟮鸟詈稀C嫦驅(qū)ο蟮母拍詈完P(guān)系數(shù)據(jù)之間的映射成為單個(gè)組件的職責(zé),該組件可以獨(dú)立于應(yīng)用程序和域?qū)ο笮薷摹ο?關(guān)系映射通常使用對應(yīng)程序隱藏其全部映射細(xì)節(jié)的抽象定義。圖3說明了對象/關(guān)系映射抽象和實(shí)現(xiàn)如何解除耦合:
對象/關(guān)系映射實(shí)現(xiàn)負(fù)責(zé)域?qū)ο蠛完P(guān)系數(shù)據(jù)之間的映射,通常直接與物理數(shù)據(jù)庫驅(qū)動(dòng)程序交互。一種方法是定義定制的實(shí)現(xiàn),硬編碼域?qū)ο蟆?yīng)用程序或者系統(tǒng)的全部映射細(xì)節(jié)。在代碼中硬編碼細(xì)節(jié)不容易修改,但與把同樣的映射埋藏在應(yīng)用程序代碼或域?qū)ο笾邢啾龋峁┝烁逦慕怦睢_@種方法也可以作為一種有效的原型策略,在構(gòu)造功能更完善的解決方案之前開發(fā)應(yīng)用程序。
復(fù)雜的對象/關(guān)系映射系統(tǒng)使用元數(shù)據(jù)定義映射的細(xì)節(jié)。元數(shù)據(jù)使得實(shí)現(xiàn)實(shí)現(xiàn)非常通用,因?yàn)樾薷挠成涞募?xì)節(jié)不需要升級或重新編譯應(yīng)用程序和域?qū)ο蟆MǔS成湓獢?shù)據(jù)保存在配置文件或數(shù)據(jù)庫中。同樣普遍的做法是定義管理工具,使用戶無需要解持久元數(shù)據(jù)格式也能查看和更新映射細(xì)節(jié)。
對象/關(guān)系映射系統(tǒng)要解決的最基本的問題是,如何從面向?qū)ο蟮母拍钣成涞疥P(guān)系數(shù)據(jù)庫的概念。表1描述了一種通用的類比。這些只是一般的原則,并不是必需的。可以為應(yīng)用程序使用的每個(gè)表定義一個(gè)類。應(yīng)用程序或者對象/關(guān)系映射為引用的每個(gè)行實(shí)例化一個(gè)對象實(shí)例。每個(gè)域?qū)ο蠖际褂脤傩怨_類似的列值。
面向?qū)ο蟮母拍?/p> | 關(guān)系數(shù)據(jù)庫的概念 |
類 | 表 |
對象 | 行 |
屬性 | 列 |
表1 面向?qū)ο蠛完P(guān)系數(shù)據(jù)庫的一般類比
實(shí)際上并不總存在直接的對應(yīng)關(guān)系。比如,對象/關(guān)系映射常常隱藏應(yīng)用程序不需要的關(guān)系數(shù)據(jù),如單純用于定義表關(guān)系的鍵或者標(biāo)識屬性。此外對象/關(guān)系映射可以代表應(yīng)用程序進(jìn)行雙向數(shù)據(jù)轉(zhuǎn)換或者計(jì)算屬性,甚至封裝更多的數(shù)據(jù)模型細(xì)節(jié)。如果需要定義的域?qū)ο蟾h(yuǎn)地偏離這種類比,必須保證選擇的對象/關(guān)系映射系統(tǒng)支持要求的映射能力。
一旦定義了對象/關(guān)系映射,訂單處理程序中生成發(fā)票的步驟就很容易描述了:
1.請求訂單--應(yīng)用程序請求相關(guān)的訂單,對象/關(guān)系映射用Order對象集合返回訂單。
2.獲取訂單的每個(gè)條目--可以直接在Order對象中取得。要記住在面向?qū)ο笮g(shù)語中,Order包含了LineItems集合,并且Order類把該集合作為一個(gè)屬性公開。
3.計(jì)算并報(bào)告--像以前那樣計(jì)算總價(jià)并打印發(fā)票。
注意,應(yīng)用程序完全使用域?qū)ο蟛僮鳎瑳]有任何對關(guān)系數(shù)據(jù)模型或數(shù)據(jù)訪問的明確引用。發(fā)生的物理數(shù)據(jù)庫操作與前述耦合的方法相同,只不過這一次是由對象/關(guān)系映射幕后發(fā)出的。
解決像訂單處理應(yīng)用程序所要求的這種簡單的情形,實(shí)現(xiàn)一個(gè)對象/關(guān)系映射是完全可以接受的。但是如果準(zhǔn)備在應(yīng)用程序中廣泛使用對象/關(guān)系映射,你可能不愿意接受這種挑戰(zhàn)--從零開始創(chuàng)建一種通用的實(shí)現(xiàn)。編寫一個(gè)高效、通用、元數(shù)據(jù)驅(qū)動(dòng)的對象/關(guān)系映射是一項(xiàng)困難的任務(wù)。所幸,有許多商業(yè)化的產(chǎn)品和標(biāo)準(zhǔn)可以直接在應(yīng)用程序中使用。多數(shù)這類產(chǎn)品允許使用工具或配置文件定義映射元數(shù)據(jù),為物理數(shù)據(jù)庫訪問插接不同的數(shù)據(jù)庫驅(qū)動(dòng)程序。
適用性
對象/關(guān)系映射模式適用于以下情況:
需要向應(yīng)用程序邏輯和域?qū)ο箅[藏物理數(shù)據(jù)模型和數(shù)據(jù)訪問的復(fù)雜性。這樣做可以使這些成分更加整潔,集中處理所建模的業(yè)務(wù)對象和過程。
需要在單個(gè)組件中封裝域?qū)ο笥成洌员隳軌蜻m應(yīng)數(shù)據(jù)模型的變化而不必修改應(yīng)用程序代碼或者域?qū)ο蠖x。
需要從域?qū)ο笥成涞蕉喾N數(shù)據(jù)模型而不修改應(yīng)用程序代碼或域?qū)ο蠖x的通用性。這種通用使應(yīng)用程序能夠與多種數(shù)據(jù)模型結(jié)合,不管它們是怎么定義的。
結(jié)構(gòu)
圖4說明了一種對象/關(guān)系映射實(shí)現(xiàn)的靜態(tài)結(jié)構(gòu)。PersistenceManager接口按照一般的域?qū)ο蠖x定義數(shù)據(jù)庫操作。它通常定義讀寫和刪除與對象的操作。這種接口不需要公開任何數(shù)據(jù)庫細(xì)節(jié)。也可以把持久性操作集合分散到多個(gè)接口,一起形成概念上的PersistenceManager抽象。
ConcretePersistenceManager依據(jù)物理數(shù)據(jù)庫操作提供了這些操作的實(shí)現(xiàn)。它引用某種形式的Map
data,后者描述了域?qū)ο笥成洹K彩褂昧薖hysicalDatabaseDriver與關(guān)系數(shù)據(jù)庫交互。
ConcretePersistenceManager為應(yīng)用程序封裝了數(shù)據(jù)模型、數(shù)據(jù)訪問和域?qū)ο笥成洹?br />在對象/關(guān)系映射標(biāo)準(zhǔn)和商業(yè)對象/關(guān)系映射系統(tǒng)中,存在這種結(jié)構(gòu)的幾種變體。一種常見的變體是,通過隱式的框架和環(huán)境用從PersistenceManager中解耦應(yīng)用程序代碼。其他的變體代替應(yīng)用程序生成調(diào)用的映射代碼。
交互
圖5說明了應(yīng)用程序?qū)oncretePersistenceManager調(diào)用讀操作時(shí)的情形。ConcretePersistenceManager找到描述映射細(xì)節(jié)的相關(guān)元數(shù)據(jù),使用這些信息向物理數(shù)據(jù)庫驅(qū)動(dòng)程序 發(fā)出操作指令。最后,它使用關(guān)系數(shù)據(jù)和元數(shù)據(jù)創(chuàng)建一個(gè)新的域?qū)ο蟛堰@個(gè)對象返回給調(diào)用者。其他PersistenceManager操作的工作原理類似。
6.效果
對象/關(guān)系映射模式有如下效果:
權(quán)衡
依賴于另外的商品化產(chǎn)品--多數(shù)應(yīng)用程序都要使用一個(gè)商品化的數(shù)據(jù)庫、一個(gè)或多個(gè)數(shù)據(jù)庫驅(qū)動(dòng)程序和一個(gè)應(yīng)用程序服務(wù)器。如果大量地使用對象/關(guān)系映射,那么再集成一個(gè)商品化的對象/關(guān)系映射產(chǎn)品或許也是有利的。購買商品化產(chǎn)品可以顯著地降低開發(fā)成本。但是重新發(fā)布第三方軟件確實(shí)會(huì)帶來另外的法律、組裝和安裝的問題。
優(yōu)點(diǎn)
清晰的應(yīng)用程序代碼--與包含數(shù)據(jù)模型和數(shù)據(jù)訪問細(xì)節(jié)的代碼相比,單純處理域?qū)ο蟮膽?yīng)用程序代碼更加清晰,也更容易開發(fā)和維護(hù)。如果使用定義良好的、只公開邏輯操作的域?qū)ο螅瑧?yīng)用程序的代碼就會(huì)更集中于它自身的業(yè)務(wù)邏輯。此外,當(dāng)以后改變數(shù)據(jù)模型或數(shù)據(jù)訪問細(xì)節(jié)時(shí)也會(huì)處在更有利的位置。
映射到替換的數(shù)據(jù)模型--最通用的對象/關(guān)系映射機(jī)制隔離了可配置的映射元數(shù)據(jù),可以在不影響應(yīng)用程序代碼的情況下修改元數(shù)據(jù)。許多對象/關(guān)系產(chǎn)品在運(yùn)行時(shí)保存和解釋映射元數(shù)據(jù),元數(shù)據(jù)的變化不需要重新編譯任何代碼。
映射到替換數(shù)據(jù)模型的概念具有很大的價(jià)值。通常數(shù)據(jù)模型都捆綁到應(yīng)用程序上。這個(gè)特點(diǎn)嚴(yán)重地制約了數(shù)據(jù)模型的變更,國為需要廣泛地升級軟件。把數(shù)據(jù)模型細(xì)節(jié)封裝在元數(shù)據(jù)中允許改變數(shù)據(jù)模型,比如重新安排表、轉(zhuǎn)移不同的平臺,甚至轉(zhuǎn)移到像基于XML的數(shù)據(jù)庫或者面向?qū)ο髷?shù)據(jù)庫這樣不同類型的數(shù)據(jù)存儲(chǔ)。
元數(shù)據(jù)也非常有益,可以很快使應(yīng)用程序變化了的數(shù)據(jù)模型。從銷售和演示的角度看這一點(diǎn)尤其重要,因?yàn)榭梢钥焖倥渲脩?yīng)用程序使其處理預(yù)期客戶的遺留數(shù)據(jù)。當(dāng)看到演示是在他們自己的數(shù)據(jù)環(huán)境下進(jìn)行的時(shí),潛在的客戶可能會(huì)更感興趣。
缺點(diǎn)
限制了應(yīng)用程序?qū)?shù)據(jù)訪問的控制--應(yīng)用程序代碼只能訪問PersisenceManager接口定義的操作。因?yàn)镃oncretePersistenceManager封裝了數(shù)據(jù)訪問的性能。如果采用商業(yè)對象/關(guān)系映射產(chǎn)品情況就更是如此。
7.策略
如果使用對象/關(guān)系映射機(jī)制管理域?qū)ο螅侠淼霓k法是在整個(gè)應(yīng)用程序中使用同樣的策略。對于從整體上理解和分析應(yīng)用程序的數(shù)據(jù)庫交互,一致的數(shù)據(jù)訪問是有好處的。這一節(jié)討論實(shí)現(xiàn)對象/關(guān)系映射模式中可能遇到的各種問題。
非映射屬性
一般而言,參與對象/關(guān)系映射實(shí)例的域?qū)ο髮?yīng)一個(gè)物理數(shù)據(jù)庫表或者聯(lián)接表中的一行。但是并不是域?qū)ο蟮乃袑傩远夹枰4嬖跀?shù)據(jù)庫中。有時(shí)候,它們是根據(jù)其他來源計(jì)算或轉(zhuǎn)化而成的。這些屬性稱為非映射屬性,因?yàn)椴皇侵苯佑成錇殛P(guān)系數(shù)據(jù)的一部分。非映射屬性一般不會(huì)引起另外的映射問題,可以用域?qū)ο蟮挠成鋵傩宰詣?dòng)處理和計(jì)算。比如LineItem對象可能公開一個(gè)totalPrice非映射屬性,根據(jù)它的映射屬性unitPrice和quantity計(jì)算得到。