將對象映射到關(guān)系數(shù)據(jù)庫_第1頁
將對象映射到關(guān)系數(shù)據(jù)庫_第2頁
將對象映射到關(guān)系數(shù)據(jù)庫_第3頁
將對象映射到關(guān)系數(shù)據(jù)庫_第4頁
將對象映射到關(guān)系數(shù)據(jù)庫_第5頁
已閱讀5頁,還剩29頁未讀, 繼續(xù)免費閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報或認(rèn)領(lǐng)

文檔簡介

1、將對象映射到關(guān)系數(shù)據(jù)庫:對象/ 關(guān)系映射(O/R Mapping)詳解大多數(shù)現(xiàn)代商業(yè)應(yīng)用開發(fā)項目使用面向?qū)ο蠹夹g(shù),比如采用Java或者C#來創(chuàng)建應(yīng)用軟件,同時使用關(guān)系型數(shù)據(jù)庫來存儲數(shù)據(jù)。但這并不是要說你沒有其它選擇, 也有許多應(yīng)用程序是使用面向過程的語言開發(fā),比如COBOL,而且也有許多系統(tǒng) 使用對象型數(shù)據(jù)庫或者XML數(shù)據(jù)庫來存儲數(shù)據(jù)。然而,因為面向?qū)ο蠛完P(guān)系數(shù)據(jù) 庫技術(shù)到目前為止已經(jīng)成為一種事實上的標(biāo)準(zhǔn),在本章節(jié)中我假設(shè)你正在使用這 些技術(shù)。如果你采用其它的存儲技術(shù),本文里的許多概念仍然適用,只需要做一點點修改(不必?fù)?dān)心,Realistic XML總括了對象與XML映射的相關(guān)問題)。在項目組

2、通常用來創(chuàng)建以軟件為基礎(chǔ)的系統(tǒng)時,那些技術(shù)中,存在著面向?qū)ο蠹?術(shù)和關(guān)系型技術(shù)之間的阻抗失配。不過這種阻抗失配很容易被克服,秘訣在于兩 點:你需要理解把對象映射到關(guān)系型數(shù)據(jù)庫的過程,以及如何去實現(xiàn)這些映射。 在本章節(jié)里,“映射”一詞是用來表示如何把對象和對象之間的關(guān)系對應(yīng)到數(shù)據(jù) 庫表以及表之間的關(guān)系。你將很快發(fā)現(xiàn)這并不像聽起來的那樣簡單易懂,盡管它 實際上也不是那么的槽糕。目錄敏捷DBA的角色基本概念oShadow信息o映射元數(shù)據(jù)o如何使映射適合全過程繼承結(jié)構(gòu)的映射o整個層次結(jié)構(gòu)映射到一張表o每個具體類映射到單獨的一張表o每個類單獨映射到一張表o將類映射為一個通用的表結(jié)構(gòu)o多重繼承映射 o映射

3、策略之間的比較映射對象關(guān)系o關(guān)系的類型o如何實現(xiàn)對象關(guān)系o如何實現(xiàn)關(guān)系數(shù)據(jù)庫中的關(guān)系o關(guān)系映射一對一映射一對多映射多對多映射o映射有序集合o映射遞歸關(guān)系映射類作用域(Class-Scope )屬性性能調(diào)優(yōu)o優(yōu)化你的映射o延遲讀取為什么數(shù)據(jù)Schema不應(yīng)該主導(dǎo)對象Schema實現(xiàn)方式對對象的影響模型驅(qū)動體系結(jié)構(gòu)(MDA: Model Driven Architecture)的含義映射技術(shù)模式化參考文獻(xiàn)和閱讀推薦1. 敏捷 DBA 的角色圖 1 顯示了一個敏捷 DBA 在映射對象到關(guān)系數(shù)據(jù)庫的過程中所扮演的角色。其中我們關(guān)心 三個主要的活動。1.映射?;镜哪繕?biāo)是決定一個有效的策略來持久化對象數(shù)

4、據(jù)。這包括保存單個對象 的屬性以及對象之間的關(guān)聯(lián),同時也包括那些類之間的繼承結(jié)構(gòu)。2.實現(xiàn)映射3.性能調(diào)優(yōu)在圖 1 中我們注意到有一個有趣的事情,敏捷的 DBA 和應(yīng)用程序開發(fā)人員在在這 三個主要活動中都在一起工作。雖然敏捷的 DBA 應(yīng)該確保映射的有效性,但他們 實際上并不是獨自對其負(fù)責(zé)的。與他人協(xié)同工作而不單打獨斗正是敏捷軟件開發(fā) 成功的關(guān)鍵所在。圖 1. 映射時敏捷 DBA 的角色。2. 基本概念在學(xué)習(xí)如何把對象映射到關(guān)系型數(shù)據(jù)庫的過程中,通常是從映射一個類的數(shù)據(jù)屬 性開始的。一個屬性可以映射到關(guān)系型數(shù)據(jù)庫里 0 個或者多個字段。請記住,不 是所有的屬性都是持久性的,其中的一些只是用做臨

5、時計算的。例如,在你的應(yīng) 用程序中一個 Student 對象可能需要有一個平均分(averageMark)屬性,但并 不需要存儲到數(shù)據(jù)庫里,因為它是由應(yīng)用程序計算得到。一個對象的某些屬性可 能本身也是對象,比方說一個 Customer 對象擁有一個 Address 對象作為其屬性這其實反映了兩個類之間需要被映射的關(guān)系,Address 類本身也需要被映 射。重要的是這是一個遞歸的定義:在需要的地方,一個屬性將被映射到 0 個或者多個字段。最簡單的映射就是把一個屬性映射到一個字段。當(dāng)雙方擁有一樣的基本類型的時 候,這甚至可以變得更簡單。例如,雙方都是 date 類型,或者屬性是 string 類型

6、而字段是 char 型,或者屬性是 number 類型而字段是 float 類型。映射術(shù)語映射 (動詞).指的是如何把對象和對象之間的關(guān)系持久化到永久存儲設(shè)備(這在里是關(guān)系型數(shù)據(jù)庫)中的行為。映射 (名詞).如何將對象的屬性或關(guān)系持久化到永久存儲設(shè)備的定義的關(guān)系。屬性. 數(shù)據(jù)屬性,是實際的物理屬性,例如一個 firstName 字串;或者是由某個操作實現(xiàn)的虛擬屬性,例如 getTotal()方法返回一個訂單的總數(shù)。屬性映射. 描述如何持久化對象的屬性的映射。關(guān)系映射.描述如何持久化兩個或者更多的對象之間的一個關(guān)系(關(guān)聯(lián),聚合或者組合)。把類映射到表上會讓許多事情思考起來更簡單,有時候的確是這樣

7、映射的,但并非總是這樣直接,除了少數(shù)特別簡單的數(shù)據(jù)庫,你將不會有機會在類和表之間進(jìn) 行簡單的一一映射。在這章的后面你將看到繼承映射。但是,就這一整章全面來 看,通常對于初始的映射,單類映射到單表是適用的(性能調(diào)優(yōu)可能會促使你對映射進(jìn)行重構(gòu))。現(xiàn)在,讓我們從簡單事物開始。圖 2 顯示了 2 個模型,一個UML的類圖和一個遵 循UML數(shù)據(jù)建模規(guī)則的物理數(shù)據(jù)模型。這兩張圖描繪了一個簡單的訂單系統(tǒng)。你 可以看到如何映射類的屬性到數(shù)據(jù)庫的字段上。例如,圖里顯示Order類的 dateFulfilled屬性映射到Order表的dateFulfilled字段,OrderItem類的 numberOrdere

8、d屬性映射到OrderItem表的NumberOrdered字段。圖 2.簡單映射的例子基本屬性的映射很容易確定,有幾個原因。首先,兩個模型中使用相似的命名規(guī) 則,這是采用敏捷建模實踐“建模標(biāo)準(zhǔn)化”的一個方面;其次,通常是同一群人 創(chuàng)建這兩個模型。而當(dāng)人們在不同的團(tuán)隊工作的時候,很容易做出不同的方案, 即便是在各個團(tuán)隊本身的工作都很出色的時候也是這樣,因為他們沿著不同的方 向進(jìn)行決策;第三,一個模型很容易用來驅(qū)動另一個模型的開發(fā)。在“不同的項 目需要不同策略”一文中我討論了當(dāng)你創(chuàng)建一個新系統(tǒng)的時候,你的對象schema 應(yīng)該主導(dǎo)你的數(shù)據(jù)庫schema的開發(fā)。圖 2 里面顯示的兩個 schema

9、 雖然很相似,但還是存在一些區(qū)別。這些區(qū)別意味 著不存在一個完美的映射。2 個 schema 間的不同點有:在對象 schema 里,tax 有多個屬性而數(shù)據(jù) schema 里只有一個。當(dāng)對象被保存時, Order 類里 tax 的 3 個屬性將被相加并保存到 tax 字段里。然而,當(dāng)對象被讀進(jìn)內(nèi)存 時,這 3 個屬性將需要被計算(或者使用一個延遲初始化的方式,這樣每個屬性僅 僅在第一次被訪問的時候計算)。一個像這樣的 schema 上的區(qū)別說明數(shù)據(jù)庫 schema 需要重構(gòu),把 tax 字段分成 3 個不同的字段。數(shù)據(jù)Schema標(biāo)明了鍵而對象Schema沒有。表中的每一行都有一個全表唯一的

10、主鍵 值,行間的關(guān)系被用外鍵實現(xiàn)。而對于對象之間的關(guān)系,是通過使用引用而非使用 外鍵。這暗示為了完整的持久化對象和它們的關(guān)系,對象需要知道數(shù)據(jù)庫里面用來 標(biāo)識它們的鍵值。這些額外的信息被稱為“shadow 信息”。每個 Schema 里面使用了不同的類型。Order 里 subTotalBeforeTax 屬性是 Currency類型,而 Order 表里 subTotalBeforeTax 字段是 float 型。當(dāng)你實現(xiàn)這個映射,你需要 在這些數(shù)據(jù)的表示形式之間進(jìn)行無損轉(zhuǎn)換。2.1 Shadow 信息Shadow信息是指那些為了將對象持久化,而不得不維持的非業(yè)務(wù)數(shù)據(jù)。這通常 包括主鍵信息,

11、特別是當(dāng)主鍵是沒有業(yè)務(wù)含義的代理鍵值時;并發(fā)控制標(biāo)識例如 時間戳或者增量計數(shù)器;以及那些版本號。例如,在圖 2 你可以看到Order表有 一個OrderID的字段作為主鍵,一個Order類所沒有的LastUpdate字段被用來樂觀 并發(fā)控制。為了正確持久化一個order對象,就需要實現(xiàn)包含這些信息的shadow 屬性。圖 3 顯示一個對 Order 和 OrderItem 進(jìn)行詳細(xì)設(shè)計的類模型。和圖 2 相比,有一 些修改。首先,新的圖顯示了類需要正確持久化自己所需要的 shadow 屬性。 shadow 屬性是實現(xiàn)可見的,在它們的名字前面是一個空格而不是一個“”號, 同時被指定了進(jìn)行說明(這

12、不是一個 UML 標(biāo)準(zhǔn))。第二,它顯 示需要在兩個類之間實現(xiàn)聯(lián)系所需要的輔助(scaffolding)屬性。輔助屬性,例如 Order 里面的 orderItems 列表,同樣是實現(xiàn)可見的。第三,一個 getTotalTax() 操作需要被加到 Order 類里來計算 Order 表中 tax 字段所需要的值。這是為什 么我用屬性映射這個詞來代替屬性映射你所想要做的是映射一個類里面的屬 性到數(shù)據(jù)庫里的字段上,有時這些屬性是通過簡單的屬性實現(xiàn)的,而其它某些時 候是由一個或者多個操作所決定的。圖 3. 在一個類圖里包含shadow 信息我還沒有討論的一種 shadow 信息是用一個 boolean

13、 類型的標(biāo)志來表示當(dāng)前一個對象是否存在于數(shù)據(jù)庫中。這里的問題是當(dāng)你把數(shù)據(jù)保存到一個關(guān)系型數(shù)據(jù)中, 如果原先的對象是從數(shù)據(jù)庫中獲取出來的,你需要使用一個 SQL update 語句來 保存數(shù)據(jù),否則應(yīng)該使用 SQL insert 語句。一個普通的解決方法是為每個類實 現(xiàn)一個 isPersistent 的 boolean 型信號標(biāo)志(圖 3 里沒有顯示),當(dāng)數(shù)據(jù)是從 數(shù)據(jù)庫里面讀取的時候把它的值設(shè)置成 true,如果對象是新創(chuàng)建的話則設(shè)置為false。在UML社區(qū)里面的一個通用的風(fēng)格約定是在類圖里不顯示shadow信息,例如鍵值 或并發(fā)標(biāo)識。類似的,通常也不顯示支撐代碼。因為每個人都知道你需要做這

14、種 事情,所以何必浪費時間去顯示這些明顯的事實呢?Shadow信息不必用業(yè)務(wù)對象(business object)來實現(xiàn),不過那樣你的程序就 要在其他地方處理這個問題。例如,在Enterprise JavaBeans (EJBs)里你把主 鍵保存在EJB以外的主鍵類(primary key class)里,獨立對象引用相關(guān)的主鍵 對象。而進(jìn)一步的,Java Data Object(JDO)則是在JDOs里面實現(xiàn)shadow 信息, 而不是在業(yè)務(wù)對象(business object)里。2.2 映射元數(shù)據(jù)圖 4 顯示了元數(shù)據(jù)(meta data),這些是代表持久化圖 3 里Order和Order

15、Item 類所需要的屬性映射。元數(shù)據(jù)是關(guān)于數(shù)據(jù)的信息。因如下原因使得圖 4 顯得很重 要的。首先,我們需要某個方式來表現(xiàn)映射。我們可以把 2 個schema并排放在一 起,就像圖 2 里面那樣,然后在它們之間畫線,但是這很快會變的非常復(fù)雜,而 另外一個選擇是像圖 4 里面這樣列表;第二,映射元數(shù)據(jù)的概念對持久化框架的 功能是非常重要的,這是一個可以讓敏捷數(shù)據(jù)庫技術(shù)發(fā)揮作用的數(shù)據(jù)庫封裝策略。圖 4.代表屬性映射的元數(shù)據(jù)PropertyColumnOrder.orderIDOrder.OrderIDOrder.dateOrderedOrder.DateOrderedOrder.dateFulfil

16、ledOrder.DateFulfilledOrder.getTotalTax()Order.TaxOrder.subtotalBeforeTaxOrder.SubtotalBeforeTaxOrder.shipTo.personIDOrder.ShipToContactIDOrder.billTo.personIDOrder.BillToContactIDOrder.lastUpdateOrder.LastUpdateOrderItem.orderedOrderItem.OrderIDOrder.orderItems.position(orderItem)OrderItem.ItemSequ

17、enceOrderItem.item.numberOrderItem.ItemNoOrderItem.numberOrderedOrderItem.NumberOrderedOrderItem.lastUpdateOrderItem.LastUpdate我采用的命名規(guī)則是非常直接了當(dāng)?shù)模篛rder.dateOrdered 指得是 Order 類里的 dateOrdered 屬性。類似的還有,Order.DateOrdered 指得是 Order 表里的 DateOrdered 字段。Order.getTotalTax()指得是 Order 里的 getTotalTax() 操 作而 則是被 O

18、rder.billTo屬性引用的 Person 對象里 的 personID 屬性??雌饋碜铍y理解的屬性是 Order.orderItems.position(orderItem),它指向在將要保存的 OrderItem 實例里 Order.orderItems 列表中的位置。圖 4 暗示了面向?qū)ο蠹夹g(shù)和關(guān)系型技術(shù)之間一個最重要的阻抗失配。Class同時 實現(xiàn)行為和數(shù)據(jù),而關(guān)系型數(shù)據(jù)庫的表僅僅保存數(shù)據(jù)而已。這導(dǎo)致當(dāng)你映射一個 類的屬性到關(guān)系型數(shù)據(jù)庫時,你也需要映射那些操作到數(shù)據(jù)庫字段上,例如: getTotalTax() 和position()。雖然在這個例子里面沒有出現(xiàn),但是你常常需要 映射

19、僅代表一個屬性兩個操作(operation)到一個字段一個操作是設(shè)置值 如:setFirstName(),而另外一個是獲取值,如getFirstName().這些操作通常 分別被稱作setter和getter,或者mutator和accessor.無論何時,一個鍵值都要被映射到類里的一個屬性上,例如在 OrderItem.ItemSequence 和 Order.orderItems.position(orderItem)之間的 映射,這實際是關(guān)系映射的一部分工作,將在本章的后面進(jìn)行討論。這是因為在 關(guān)系數(shù)據(jù)庫里通過使用鍵值來實現(xiàn)數(shù)據(jù)間的聯(lián)系。2.3 如何使映射適合全過程參看文章。3. 繼承結(jié)

20、構(gòu)的映射由于關(guān)系數(shù)據(jù)庫不是生來就支持繼承的,這就強制你必須將對象schema的繼承結(jié) 構(gòu)映射到相應(yīng)的數(shù)據(jù)庫schema中去。多半是因為不牢靠的基類(譯注:不牢靠的 基類是指,有時基類很難修改,因為一旦修改基類,子類就容易出錯)的原因,面向?qū)ο笊鐓^(qū)不太提倡使用繼承,而我的經(jīng)驗表明:之所以出現(xiàn)這個問題,是因 為面向?qū)ο蟮拈_發(fā)者們大多缺乏封裝的技巧,而非繼承概念本身出了問題(Ambler 2001a)。我想說的是,事實上,你只需要做少量的工作,即可將一個繼承層次映射到關(guān)系數(shù)據(jù)庫中去,而這并不會影響你在合適的地方運用繼承。把對象存入關(guān)系數(shù)據(jù)庫時,繼承的概念會帶來一些有趣的變化。如何在數(shù)據(jù)模型 中組織那

21、些通過繼承而得到的屬性?本節(jié)中,你將看到用來解決如何將繼承關(guān)系 映射到數(shù)據(jù)庫的三種基本方法,以及第四種補充方法,它不限于繼承映射。這些 方法如下所示:整個類層次結(jié)構(gòu)映射到一張表每個具體類單獨映射到一張表每個類單獨映射到一張表所有類映射到一個通用的表結(jié)構(gòu)如圖 6 所示,有兩個版本的類層次結(jié)構(gòu),我們將通過討論如何映射這兩個結(jié)構(gòu),來深入了解每種方法。第一個版本描述了三個類:一個抽象類 Person,和兩個具 體類 Employee 和 Customer。之所以知道 Person 是抽象類,是因為在圖中它用斜 體表示。在較早版本的 UML 中,會用約束“abstract”來表示抽象類。第二個版 本在第

22、一個版本的基礎(chǔ)上,往類層次結(jié)構(gòu)中添加了一個新的具體類 Executive。 旨在描述,當(dāng)實現(xiàn)了第一個類層次結(jié)構(gòu)之后,有了一個新的需求,要求為雇員中 的執(zhí)行主管,而非普通雇員,頒發(fā)固定的年度分紅。類 Executive 就是為了滿足這一新功能而添加的。簡單起見,我沒有對這些類的所有屬性、屬性的完整簽名,以及類的任何操作進(jìn) 行建模。而這幅類圖恰好足以滿足我的目的,換句話說,這是一個敏捷的模型。 此外,這些類層次結(jié)構(gòu)本應(yīng)該用分析模式中的Party模式(Fowler 1997)或Business Entity模式(Ambler 1997)。我并沒有這么做,因為在這里,我并不是為了說明 分析模式的有效應(yīng)

23、用,而是用一個簡單的例子來說明繼承層次結(jié)構(gòu)的映射我 總是遵循敏捷建模(AM)的“每次只針對一個目標(biāo)建模”(Model With A Purpose) 的原則。圖 6. 一個簡單類層次結(jié)構(gòu)的兩個版本誤用繼承也會帶來問題比如,圖 11.6 的層次結(jié)構(gòu)本應(yīng)該通過Party (Hay 1996, Fowler1997) 模式或Business Entity (Ambler 1997) 模式進(jìn)行更好的建模。舉例來說,有人可能既是 雇員又是顧客,為此你要在內(nèi)存中保留多個對象,而這可能會給你的應(yīng)用程序帶來問題。我 選擇這個例子,是因為我需要一個簡單的,易于理解的類層次結(jié)構(gòu)來進(jìn)行映射。3.1 整個層次結(jié)構(gòu)映射

24、到一張表按照此策略,把所有類的所有屬性都存儲到一張表中去。當(dāng)采用這種方法時,圖6 中的類層次結(jié)構(gòu)對應(yīng)的數(shù)據(jù)模型如圖 7 所示。這是非常直觀的方式,每個類的 屬性都存儲到表 Person 中,表名最好用類層次結(jié)構(gòu)中根類的名字來命名。圖 7. 映射到一張表表里多加了兩個字段PersonPOID 和 PersonType 。圖中,衍型(stereotype)說明第一個字段是表的主鍵,第二個字段是標(biāo)識代碼,用來指明一個人是 顧客還是雇員,抑或兩者皆是。PersonPOID是一個代理鍵(surrogate key), 它是持久化對象的標(biāo)識(POID,persistent object identifie

25、r),通常簡稱為 對象標(biāo)識(OID,object identifier)。本應(yīng)該使用可選衍型(stereotype)來標(biāo)示的,但POID已經(jīng)暗示了這層意思,這表明,這類衍型(stereotype)只會無謂地使我們的類圖更復(fù)雜(參見AM實踐“簡單地描述模型”(Depict Models Simply))。數(shù)據(jù)建模101( )詳細(xì)討論了 代理鍵(surrogate keys)的相關(guān)內(nèi)容。用來識別對象類型的字段PersonType是必需的,這個對象可以由給定的數(shù)據(jù)庫中 的一行數(shù)據(jù)實例化而來。例如,取值為E表示該人是雇員,C表示是顧客,B則表 示既是雇員又是顧客。這種方法看似直觀,但當(dāng)類型數(shù)目和類型間

26、聯(lián)合越來越多 的時候,這種方法就漸漸變的力不從心了。例如,添加執(zhí)行主管的概念,需要添 加一個碼值,比如以X來代表。對于值B來說,它代表的是既是雇員又是顧客,此 時就顯得有點不倫不類了。此外,有些(類間)聯(lián)合中可能會包括執(zhí)行主管,比 如,一個人既是執(zhí)行主管又是顧客也是很合乎情理的事情,這種情形也需要一個 碼值來表示。對于各種類型聯(lián)合的情況,應(yīng)該考慮使用“用布爾值來代替類型碼”(Replace Type Code With Booleans)的數(shù)據(jù)庫重構(gòu)技法,如圖8所示。為了簡單,沒有包含那些需要并發(fā)控制的字段,比如位于圖 3 表中的時間戳字段, 同時,用于數(shù)據(jù)版本跟蹤的字段也沒有包括在內(nèi)。圖 8

27、. 一種重構(gòu)方法3.2 每個具體類映射到單獨的一張表這種方法為每個具體類創(chuàng)建一張表,每張表既包括對應(yīng)類自己實現(xiàn)的那些屬性, 也包括它繼承下來的那些屬性。采用這種映射方法,圖 6 中的類層次結(jié)構(gòu)所對應(yīng) 的數(shù)據(jù)庫物理數(shù)據(jù)模型如圖 9 所示。每個具體類 Customer 和 Employee 都有對 應(yīng)的映射表,對象從這些表中被實例化,而抽象類 Person 則沒有對應(yīng)的表。分 別為每張表分配了對應(yīng)的主鍵,customerPOID 和 employeePOID。為了支持后加 的類 Executive,需要做的全部事情就是添加一張相應(yīng)的表,表中包括所有 Executive 對象所需要的屬性。圖 9.

28、把具體類映射成表3.3 每個類單獨映射到一張表遵循這個策略,為每個類創(chuàng)建一張表,每個業(yè)務(wù)屬性和任何必須的標(biāo)識信息都對 應(yīng)于表中的一個字段(還包括并發(fā)控制和版本跟蹤所需的其他字段)。將每個類 都映射成一張單獨的表,圖 6 中的類層次結(jié)構(gòu)對應(yīng)的物理數(shù)據(jù)模型如圖 10 所示。 類 Customer 的數(shù)據(jù)被存儲在兩張表 Customer 和 Person 中,因此要獲取這些數(shù) 據(jù),你需要連接這兩張表(或者分兩次讀取,每張表讀一次)。鍵的應(yīng)用很有意思。注意personPOID是如何作為所有表的主鍵來使用的。對于 Customer, Employee, 和 Executive 這些表而言,personP

29、OID既是主鍵又是 外鍵。對于Customer表,personPOID是它的主鍵,同時也作為外鍵來維系與表 Person之間的關(guān)聯(lián)。這是用 和 兩種衍型(stereotype)來表示的。 在一些較早版本的UML中,不允許為單個模型元素賦多個衍型(stereotype), 但在UML1.4 版中,取消了這一限制。圖 10. 每個類單獨映射一張表通常你可能會考慮的修改,是往表 Person 中添加一個類型字段或者布爾字段,用來表示 person 的可用子類。這個額外的花銷將使一些查詢變的更容易。在很 多情況下,添加額外的視圖(view)也是可行的選擇,我更傾向于這種方法,因為與附加類型或布爾字段相

30、比,這更易于維護(hù)。3.4 將類映射為一個通用的表結(jié)構(gòu)將繼承結(jié)構(gòu)映射到關(guān)系數(shù)據(jù)庫中去的第四種選擇是采用一種通用的,有時被稱為 元數(shù)據(jù)驅(qū)動(meta-data driven)的方法來映射你的類,這種方法并不局限于繼 承結(jié)構(gòu),它支持所有形式的映射。圖 11 中所示的,是用于存儲屬性值和遍歷繼 承結(jié)構(gòu)的數(shù)據(jù) schema。這個 schema 并不完全,例如它還可以被擴展,用來映射 關(guān)聯(lián)關(guān)系,但對于我們的目的而言是足夠用了。單個屬性的值存放在 Value 表中, 因此,如果要保存一個帶有十個業(yè)務(wù)屬性的對象,那么需要十條記錄,每個屬性 對應(yīng)一條記錄。字段 Value.ObjectPOID 用來存儲特定對象

31、的唯一標(biāo)識(這種方法 假定對所有對象采用統(tǒng)一的鍵生成策略,假若不是這樣的話,你必須適當(dāng)?shù)臄U展 這個表。) 表 AttributeType 包含了代表基本數(shù)據(jù)類型的記錄,如數(shù)據(jù),字符串,錢款,整數(shù)等等。將對象的屬性值轉(zhuǎn)換成為 varchar 類型保存到 Value.Value字段中時,會需要這一信息。圖 11. 一個用于存儲對象的通用數(shù)據(jù) schema讓我們一起來看一個例子:將單個類映射到這種schema中。若要存儲圖 3 中的類OrderItem ,表Value中要有三條記錄,一條存儲已訂購的訂單項的數(shù)目,一條 存儲OrderPOID的值,該訂單項是相應(yīng)訂單的一部分,還有一條存儲ItemPOI

32、D 的 值,這個值用來描述訂單項。如果你采用樂觀鎖的方法進(jìn)行并發(fā)控制,你也可以 考慮用第四條記錄來儲存lastUpdated這個shadow屬性。表Class將為類 OrderItem 創(chuàng)建一行記錄,表Attribute將在數(shù)據(jù)庫中為每個屬性創(chuàng)建一行記錄(在本例子中,有三行或四行記錄)現(xiàn)在,將圖 6 所示的 Person 和 Customer 之間的繼承結(jié)構(gòu)映射到這種 schema 中。 表 Inheritance 是繼承映射的關(guān)鍵。每個類將由表 Class 中的一行來表示。在表 Inheritance 中也有一行,Inheritance.SuperClassPOID 的值指向表 Class

33、中表 示類 Person 的行,Inheritance.SubClassPOID 的值指向表中表示類 Customer 的行。映射剩余的結(jié)構(gòu)層次關(guān)系,需要在 Inheritance 表中為每個繼承關(guān)系都添 加一行。3.5 多重繼承映射到目前為止,我所關(guān)注的是單根繼承層次結(jié)構(gòu)的話題,所謂的單根繼承是指,子 類,如 Customer ,直接繼承單一的父類,如 Person 。多重繼承是指一個子類 有兩個或兩個以上的直接父類,如圖 12 所示,Dragon 直接繼承了類 Bird 和 Lizard 。在面向?qū)ο笳Z言中,多重繼承通常被認(rèn)為是有問題的,自 1990 年以來, 我只見過在一個領(lǐng)域問題中使用

34、多重繼承是有意義的,因此,多數(shù)語言不支持多 重繼承。但是像 C+和 Eiffel 這樣的語言支持多重繼承,所以也存在需要將多 重繼承層次結(jié)構(gòu)映射到關(guān)系數(shù)據(jù)庫中去的情況。圖 12 展示的是,對多重繼承分別使用三種繼承映射策略而得到的結(jié)果數(shù)據(jù) schema。如圖所示,多重繼承映射也是很簡單的,跟單根繼承映射相比,并沒有 任何特別的東西。我所經(jīng)歷的最大挑戰(zhàn)是,當(dāng)將整個層次結(jié)構(gòu)映射到同一張表中 時,如何為這張表取一個合理的名字,在本例中,用 Creature 最合適。圖 12. 映射多重繼承3.6 映射策略之間的比較如表 1 所示,就這些策略而言,沒有一種策略對所有場合都是理想的。我的經(jīng)驗 表明,最容

35、易奏效的策略是,先用每個層次結(jié)構(gòu)映射一張表的策略,接下來如果 需要,就重構(gòu)相應(yīng)的 schema。有時,當(dāng)我的團(tuán)隊被“純設(shè)計方法”的工作方式 所驅(qū)動,我會先采用每個類映射一張表的策略。我盡量不使用每個具體類映射一 張表的策略,因為這樣做最后導(dǎo)致的典型結(jié)果是,需要把數(shù)據(jù)在表之間拷來拷去 的,這就強制我在項目的初期就要對它進(jìn)行合理重構(gòu)。我很少使用通用 schema 方法,很簡單,因為它不具有很好的可伸縮型。在任何應(yīng)用程序中,你都可以聯(lián)合使用前三個映射策略:每個層次結(jié)構(gòu)一個張表、 每個具體類一張表和每個類一張表, 理解這一點很重要。你甚至可以在一個大型 的層次結(jié)構(gòu)中聯(lián)合使用這三種策略。表 1.繼承映射

36、策略的比較Strategy策略Advantages優(yōu)勢Disadvantages缺陷When to Use使用的時機每個層次結(jié)構(gòu)一張表方法簡單。添加新類很方便,你只需 要為新加的數(shù)據(jù)添加新 的字段即可。通過簡單的修改行的“類 型”字段來實現(xiàn)多態(tài)。因為數(shù)據(jù)在一張表中,因 此數(shù)據(jù)的訪問速度很快。因為所有的數(shù)據(jù)在同一 張表中,特別容易生成專 門的報表。因為所有的類直接關(guān)聯(lián)到同一張表,類層次結(jié)構(gòu)內(nèi)的 耦合度增加。修改一個類將 影響整張表,而整張表的改 變又會影響類層次結(jié)構(gòu)中 的其他類。數(shù)據(jù)庫空間存在潛在浪費。 如果已有類型之間有較大的重疊,則暗示著類型復(fù)雜 化了。對大的層次結(jié)構(gòu)而言,表會 非常大。對那

37、些類層次結(jié)構(gòu)內(nèi)部的類型間不存 在或存在極少重疊 情況的簡單類層次 結(jié)構(gòu)和(或)繼承深 度比較淺的類層次 結(jié)構(gòu)而言,這種策略 很好每個具體類一張表由于單個類的所有數(shù)據(jù)都存儲在一張表中,所以 特別容易 生成專門的報 表。訪問單個對象數(shù)據(jù)的性 能高。類修改的時候必須修改它對應(yīng)的表,同時也必須修改 它所有的子類對應(yīng)的表。舉 例來說,如果你給類 Person 添加了 height 和 weight 兩 個屬性,那么你需要給表 Customer,Employee,和 Executive 添加對應(yīng)的字段。當(dāng)一個對象改變了它的角 色,比如,你雇用了你的顧 客,你需要把對象的數(shù)據(jù)復(fù) 制到相應(yīng)的表中,并為它分 配

38、一個新的 POID 值(或者 可以重用已有的 POID 值)。很難既支持多角色又能保 持?jǐn)?shù)據(jù)的完整性。比如,你 把既是顧客又是雇員的人 的名字存放到哪里?當(dāng)類型很少改變,類型之間極少重疊時, 可以選擇使用這種 策略。每個類一張因為是一一對應(yīng)的映射,每個類一張表,則在數(shù)據(jù)庫在類型之間有較大表所以容易理解。每個類型的記錄分別在 相應(yīng)的表中,因此可以很 好的支持多態(tài)。修改父類,或添加新的子 類時,只需要簡單的修改 或添加一張表。數(shù)據(jù)的大小跟對象個數(shù) 的增長成正比。中有很多表(還需要附加表用來維護(hù)類之間的關(guān)系)。使用這種方法,使得讀寫數(shù) 據(jù)需要更長的時間,因為需 要訪問多張表。如果你明智 的組織你的數(shù)

39、據(jù)庫,把一個 類層次結(jié)構(gòu)中的每張表放 入不同的物理驅(qū)動盤盤面 上(假設(shè)驅(qū)動器磁頭的所有 操作相互獨立),就可以提 高數(shù)據(jù)的讀寫速度。從數(shù)據(jù)庫生成專門的報表 特別困難,除非添加視圖來 模擬所需要的表。重疊,或類型會頻繁的修改的情況下使 用這種策略。通用schema當(dāng)用一個穩(wěn)定的 持久化框架來封裝數(shù)據(jù)庫的訪 問,可以工作的非常好。可以擴展到提供元數(shù)據(jù), 以支持包括關(guān)系映射在 內(nèi)的更大范圍的映射。簡 而言之,它是元數(shù)據(jù)映射 引擎的起點。非常靈活,可以快速的改 變存儲對象的方式,因為 只需要更新存儲在表 Class ,Inheritance , Attribute 和 AttributeType 中的

40、元數(shù)據(jù)。很先進(jìn)的技術(shù)最初可能難以實現(xiàn)。使用這種方法,需要訪問很 多數(shù)據(jù)庫的記錄來創(chuàng)建一 個對象,因此它只適用于少 量數(shù)據(jù)的情況。你可能需要一個小型的管 理程序來維護(hù)元數(shù)據(jù)。因為要訪問多行記錄來獲 取一個對象的數(shù)據(jù),因此, 用這種數(shù)據(jù)生成報表顯得 異常困難。適用于如下場合:處理少量數(shù)據(jù)的復(fù) 雜應(yīng)用程序;不常訪問數(shù)據(jù)的應(yīng) 用程序;可以預(yù)先將數(shù)據(jù)讀 入到緩存的應(yīng)用程 序。4.映射對象關(guān)系除了屬性映射與繼承映射,你還需要領(lǐng)會關(guān)系映射的藝術(shù)。有三種你需要進(jìn)行映 射的對象間關(guān)系:關(guān)聯(lián)、聚合以及組合。在這里,我將把這三種關(guān)系同等看待盡管涉及到引用完整性的時候,三者有些微妙差別,但他們的映射方式是一樣的。4.

41、1. 關(guān)系的類型在做映射時,你需要關(guān)心兩類對象關(guān)系。第一類是基于多重性(multiplicity)的, 包含三種類型:一對一關(guān)系(One-to-one relationships)。這是一種兩端多重性(multiplicity)最大值都為1 的關(guān)系(譯注:兩端最多只有一個對象) 。舉個例子來說就像圖 13 中 Employee 與 Position 之間的擁有(holds)關(guān)系。每個雇員擁有且僅擁有一個職位,每個職位可能擁 有一個雇員(有些職位還可能空缺)。一對多關(guān)系 (One-to-many relationships) 。也 被稱作 多 對一關(guān) 系 (many-to-one relatio

42、nship),這種關(guān)系產(chǎn)生于一端多重性(multiplicity)最大為 1,而另一端大于 1 的 場合。例如 Employee 與 Division 之間的隸屬(work in)關(guān)系。每個雇員在一個部門工 作,任何給定部門都有一個或者多個雇員在里面工作。多對多關(guān)系(Many-to-many relationships)。這是一種兩端多重性(multiplicity)最大值 均大于 1 的關(guān)系。例如 Employee 與 Task 之間的 分派(assigned)關(guān)系。每個雇員可以被分派一個或多個任務(wù),每個任務(wù)可以被指派給 0 個或多個雇員。第二類是基于方向(directionality)的,

43、包含兩種類型:單向關(guān)系和雙向關(guān)系。單向關(guān)系(Uni-directional relationships)。單向關(guān)系是指一個對象知道與其關(guān) 聯(lián)的其他對象,但是其他對象不知道該對象。例如,圖 13 中,Employee 和 Position 之間的擁有(holds)關(guān)系,圖中該關(guān)系是用帶開口箭頭的直線來表示的。Employee 對象知道其所擁有的職位,而 Position 對象不知道擁有它的雇員是誰(沒有需要知 道的必要)。不久你就會看到,單向關(guān)系要比雙向關(guān)系容易實現(xiàn)。雙向關(guān)系(Bi-directional relatinships)。雙向關(guān)系是指,關(guān)聯(lián)兩端的對象都彼 此知道對方。例如 Emplo

44、yee 與 Division 之間的隸屬(work in)關(guān)系。Employee 對象知道自己工作的部門,而 Division 對象也知道有哪些雇員在本部門工作。圖 13. 對象間的關(guān)系對象schema中有可能包含全部六種關(guān)系的組合。然而,關(guān)系技術(shù)并不支持單向關(guān)系的概念在關(guān)系數(shù)據(jù)庫中,所有的關(guān)聯(lián)都是雙向的,這也是對象技術(shù)與關(guān)系 技術(shù)之間阻抗失配(impedance mismatch)的一個方面。4.2.如何實現(xiàn)對象關(guān)系對象 schema 中的關(guān)系是通過對象的引用及操作來實現(xiàn)的。當(dāng)多重性 (multiplicity)是 1(比如 0.1 或者 1)的時候,這種關(guān)系通過一個對象引用、一 個 get

45、ter 操作以及一個 setter 操作來實現(xiàn)。舉例來說,圖 13 中,類 Employee 是通過組合 division 屬性、返回 division 屬性值的 getDivision()操作以及設(shè) 置 division 屬性值的 setDivision()操作來反映某個雇員隸屬于某一部門這個 事實的。用來實現(xiàn)對象關(guān)系的屬性和操作通常稱為輔助屬性和輔助操作。多重性(multiplicity)為“多”(比如 N,0.*,1.*)的關(guān)系是通過集合屬性 (collection attribute)(比如 Java 中的 Array 或者 HashSet),以及操縱該 集合的操作來實現(xiàn)的。例如,類

46、Division 定義了名叫 employees 的 HashSet 屬性、用來取值的 getEmployees()、用來設(shè)值的 setEmployees()、將一個雇員對 象 (employee)加入到 HashSet 的 addEmployee()以及從 HashSet 中刪除雇員對 象(employee)的 removeEmployee()。關(guān)系是單向的時候,只需要在“知道其它對象”的對象中實現(xiàn)代碼即可。比如 Employee 與 Position 間的單向關(guān)系,只需由類 Employee 來實現(xiàn)關(guān)聯(lián)。另一方 面,雙向關(guān)聯(lián)則需要兩個類都實現(xiàn)相關(guān)代碼,正如你在 Employee 與 Tas

47、k 之間的 多對多關(guān)系(many-to-many relationship)中看到的那樣。4.3.如何實現(xiàn)關(guān)系數(shù)據(jù)庫中的關(guān)系關(guān)系數(shù)據(jù)庫中,關(guān)系是通過使用外鍵(foreign key)來維護(hù)的。外鍵的值可能是 另一個表(某個行)鍵值的一部分,也可能就等于那個表(某個行)的鍵值。一 對一關(guān)系中需要其中一張表來定義外鍵。在圖 14 中你可以看到表 Position 包含 了 EmployeePOID,它是一個指向Employee 表的外鍵,用來實現(xiàn)兩者的關(guān)聯(lián)。換 種方式,在 Employee 表中定義 PositionPOID字段,也很簡單。圖 14. 關(guān)系數(shù)據(jù)庫中的關(guān)系要實現(xiàn)一對多關(guān)系,你需要定義

48、一個從一表指向多表的外鍵(譯注:在one-to-many 關(guān)系中,UML 里所指的 one 和 many 和數(shù)據(jù)庫里的 multiplicity 是 反過來的。比如一個 division 有幾個 employee,UML 里認(rèn)為 division 是 one 端, 因為一個 division 對應(yīng)幾個 employee;而數(shù)據(jù)庫 Entity-Relationship Diagram 里的 one 端指的是 employee,因為一個 employee 只會參與一個division-employee 關(guān)系,這個值就是所謂的 multiplicity)。 例如 Employee(多方)中就包含一

49、個 DivisionPOID 字段(外鍵),用于實現(xiàn)與 Division(一方) 的隸屬(work in)關(guān)系(注:同時,字段 DivisionPOID 在 Division 中是主鍵)。 你也可以選擇增設(shè)一個關(guān)聯(lián)表(associative table)來實現(xiàn)一對多關(guān)系(one-to-many relationship),從而很容易實現(xiàn)多對多關(guān)系(many-to-many)。在關(guān)系數(shù)據(jù)庫中,有兩種方式可以用來實現(xiàn)多對多關(guān)聯(lián)(many-to-many association)。第一種是在每個表中定義多個指向其他表的外鍵。比如要實現(xiàn) Employee 與 Task 之間的多對多關(guān)系,你可以在 Em

50、ployee 表中定義五個 TaskPOID 字段,在 Task 表中定義七個 EmployeePOID 字段。不過當(dāng)你想要賦予一個雇員超 過五項任務(wù),或者一項任務(wù)被分配給超過七個雇員時,采用這種方法就會遇到麻 煩。更好的方式是實現(xiàn)一張被稱為的關(guān)聯(lián)表(associative table)的表。正如圖14 中的 EmployeeTask 所示,關(guān)聯(lián)表包含了與其相關(guān)聯(lián)的表的主鍵的組合。采用 這種方式,你可以將同一項任務(wù)分配給五十個人,或者給同一個人指派二十項任 務(wù),完全沒有問題。這個基本“技巧”的要點在于將多對多關(guān)系轉(zhuǎn)化為兩個一對多關(guān)系,它們都與同一個關(guān)聯(lián)表相關(guān)聯(lián)。因為外鍵是用來連接表的,所以關(guān)系

51、型數(shù)據(jù)庫中的所有關(guān)系都是雙向的。這也就 是為什么你在哪張表中實現(xiàn)一對一關(guān)系都無所謂的原因,連接兩表的代碼相差無 幾。比如,圖 14 中,對于現(xiàn)有的 schema,連接兩者(Position 和 Employee) 之間的 holds 關(guān)系所使用的 SQL 代碼應(yīng)為SELECT * FROM Position, EmployeeWHERE Position.EmployeePOID = Employee.EmployeePOID假如在 Employee 表中實現(xiàn)外鍵,SQL 代碼應(yīng)為SELECT * FROM Position, EmployeeWHERE Position.PositionPO

52、ID = Employee.PositionPOID在數(shù)據(jù)庫中采用一致的鍵策略可以極大地簡化進(jìn)行關(guān)系映射所需的努力。首先是盡可能使用單字段的鍵。然后是采用某種全局唯一的代理鍵(surrogate key), 可以遵從GUID或者HIGH-LOW生成策略。這樣的好處是你只需對同一種類型的鍵值列進(jìn)行映射。既然我們明白了如何分別在兩種技術(shù)(譯注:對象技術(shù)與關(guān)系數(shù)據(jù)庫技術(shù))中實現(xiàn) 關(guān)系,那么我們來看看怎么對它們做映射。我會從對象關(guān)系到關(guān)系數(shù)據(jù)庫映射的 角度來講解。記住一件事情,某些情況下你要做出設(shè)計決策。再之,謹(jǐn)防“神奇 的 CASE 工具欄按鈕”,不要幻想它們會為你包辦一切。4.4. 關(guān)系映射關(guān)系映

53、射的一條通用的經(jīng)驗規(guī)則是你應(yīng)該使映射前后的多重性保持一致。因此一 對一的對象關(guān)系映射成一對一的數(shù)據(jù)關(guān)系,一對多的映射成一對多的,多對多的 映射成多對多的。不過世事無絕對,你也可以將一對一關(guān)系映射成一對多甚至多 對多關(guān)系。這是因為一對一關(guān)系是一對多關(guān)系的子集,一對多關(guān)系也同樣是多對 多關(guān)系的子集。圖 15 描述了圖 13 的對象 schema 與圖 14 的數(shù)據(jù)庫 schema 之間的屬性映射。注 意我只需要映射對象的業(yè)務(wù)屬性和 shadow 信息,不需要映射像 Employee.position 和 Employee.tasks 之類的輔助屬性(scaffolding attribute)。這些輔助屬性是通過映 射到數(shù)據(jù)庫中的 shadow 信息來表示的。當(dāng)關(guān)系信息被讀入內(nèi)存,儲存于主鍵字 段中的值就會被解釋成對象中相應(yīng)的 shadow 屬性。與此同時,通過為相關(guān)對象 的輔助屬性設(shè)置適當(dāng)?shù)娜≈担麈I字段所表達(dá)的關(guān)系也會被建立起來。圖 15. 屬性映射PropertyColumnPosition.titlePosition.TitlePosition.positionPOIDPosition.PositionPOIDEEmployee.NameEmployee.employeePOIDEmployee.EmployeePOI

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

最新文檔

評論

0/150

提交評論