![將對象映射到關系數(shù)據(jù)庫_第1頁](http://file3.renrendoc.com/fileroot_temp3/2022-3/2/66b7952b-d8d8-4187-bb2e-fd25a5c0d75a/66b7952b-d8d8-4187-bb2e-fd25a5c0d75a1.gif)
![將對象映射到關系數(shù)據(jù)庫_第2頁](http://file3.renrendoc.com/fileroot_temp3/2022-3/2/66b7952b-d8d8-4187-bb2e-fd25a5c0d75a/66b7952b-d8d8-4187-bb2e-fd25a5c0d75a2.gif)
![將對象映射到關系數(shù)據(jù)庫_第3頁](http://file3.renrendoc.com/fileroot_temp3/2022-3/2/66b7952b-d8d8-4187-bb2e-fd25a5c0d75a/66b7952b-d8d8-4187-bb2e-fd25a5c0d75a3.gif)
![將對象映射到關系數(shù)據(jù)庫_第4頁](http://file3.renrendoc.com/fileroot_temp3/2022-3/2/66b7952b-d8d8-4187-bb2e-fd25a5c0d75a/66b7952b-d8d8-4187-bb2e-fd25a5c0d75a4.gif)
![將對象映射到關系數(shù)據(jù)庫_第5頁](http://file3.renrendoc.com/fileroot_temp3/2022-3/2/66b7952b-d8d8-4187-bb2e-fd25a5c0d75a/66b7952b-d8d8-4187-bb2e-fd25a5c0d75a5.gif)
版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領
文檔簡介
1、將對象映射到關系數(shù)據(jù)庫:對象/ 關系映射(O/RMapping)詳解大多數(shù)現(xiàn)代商業(yè)應用開發(fā)項目使用面向對象技術,比如采用Java或者C#來創(chuàng)建應用軟件,同時使用關系型數(shù)據(jù)庫來存儲數(shù)據(jù)。但這并不是要說你沒有其它選擇,也有許多應用程序是使用面向過程的語言開發(fā),比如COBOL,而且也有許多系統(tǒng)使用對象型數(shù)據(jù)庫或者XML數(shù)據(jù)庫來存儲數(shù)據(jù)。然而,因為面向對象和關系數(shù)據(jù)庫技術到目前為止已經(jīng)成為一種事實上的標準,在本章節(jié)中我假設你正在使用這些技術。如果你采用其它的存儲技術,本文里的許多概念仍然適用,只需要做一點點修改(不必擔心,Realistic XML總括了對象與XML映射的相關問題)。在項目組通常用來創(chuàng)
2、建以軟件為基礎的系統(tǒng)時,那些技術中,存在著面向對象技術和關系型技術之間的阻抗失配。不過這種阻抗失配很容易被克服,秘訣在于兩點:你需要理解把對象映射到關系型數(shù)據(jù)庫的過程,以及如何去實現(xiàn)這些映射。在本章節(jié)里,“映射”一詞是用來表示如何把對象和對象之間的關系對應到數(shù)據(jù)庫表以及表之間的關系。你將很快發(fā)現(xiàn)這并不像聽起來的那樣簡單易懂,盡管它實際上也不是那么的槽糕。目錄敏捷DBA的角色基本概念oShadow信息o映射元數(shù)據(jù)o如何使映射適合全過程繼承結構的映射o整個層次結構映射到一張表o每個具體類映射到單獨的一張表o每個類單獨映射到一張表o將類映射為一個通用的表結構o多重繼承映射o映射策略之間的比較映射對象
3、關系o關系的類型o如何實現(xiàn)對象關系o如何實現(xiàn)關系數(shù)據(jù)庫中的關系o關系映射一對一映射一對多映射多對多映射o映射有序集合o映射遞歸關系映射類作用域(Class-Scope )屬性性能調優(yōu)o優(yōu)化你的映射o延遲讀取為什么數(shù)據(jù)Schema不應該主導對象Schema實現(xiàn)方式對對象的影響模型驅動體系結構(MDA: ModelDrivenArchitecture)的含義映射技術模式化參考文獻和閱讀推薦1.敏捷DBA的角色圖1顯示了一個敏捷DBA 在映射對象到關系數(shù)據(jù)庫的過程中所扮演的角色。其中我們關心三個主要的活動。1.映射。基本的目標是決定一個有效的策略來持久化對象數(shù)據(jù)。這包括保存單個對象的屬性以及對象之間
4、的關聯(lián),同時也包括那些類之間的繼承結構。2.實現(xiàn)映射3.性能調優(yōu)在圖1中我們注意到有一個有趣的事情,敏捷的DBA和應用程序開發(fā)人員在在這三個主要活動中都在一起工作。雖然敏捷的DBA應該確保映射的有效性,但他們實際上并不是獨自對其負責的。與他人協(xié)同工作而不單打獨斗正是敏捷軟件開發(fā)成功的關鍵所在。圖1.映射時敏捷DBA的角色。2.基本概念在學習如何把對象映射到關系型數(shù)據(jù)庫的過程中,通常是從映射一個類的數(shù)據(jù)屬性開始的。一個屬性可以映射到關系型數(shù)據(jù)庫里0個或者多個字段。請記住,不是所有的屬性都是持久性的,其中的一些只是用做臨時計算的。例如,在你的應用程序中一個Student對象可能需要有一個平均分(a
5、verageMark)屬性,但并不需要存儲到數(shù)據(jù)庫里,因為它是由應用程序計算得到。一個對象的某些屬性可能本身也是對象,比方說一個Customer對象擁有一個Address對象作為其屬性這其實反映了兩個類之間需要被映射的關系,Address類本身也需要被映射。重要的是這是一個遞歸的定義:在需要的地方,一個屬性將被映射到0個或者多個字段。最簡單的映射就是把一個屬性映射到一個字段。當雙方擁有一樣的基本類型的時候,這甚至可以變得更簡單。例如,雙方都是date類型,或者屬性是string 類型而字段是char型,或者屬性是number類型而字段是float類型。映射術語映射(動詞).指的是如何把對象和對
6、象之間的關系持久化到永久存儲設備(這在里是關系型數(shù)據(jù)庫)中的行為。映射(名詞).如何將對象的屬性或關系持久化到永久存儲設備的定義的關系。屬性. 數(shù)據(jù)屬性,是實際的物理屬性,例如一個firstName字串;或者是由某個操作實現(xiàn)的虛擬屬性,例如getTotal()方法返回一個訂單的總數(shù)。屬性映射. 描述如何持久化對象的屬性的映射。關系映射.描述如何持久化兩個或者更多的對象之間的一個關系(關聯(lián),聚合或者組合)。把類映射到表上會讓許多事情思考起來更簡單,有時候的確是這樣映射的,但并非總是這樣直接,除了少數(shù)特別簡單的數(shù)據(jù)庫,你將不會有機會在類和表之間進行簡單的一一映射。在這章的后面你將看到繼承映射。但是
7、,就這一整章全面來看,通常對于初始的映射,單類映射到單表是適用的(性能調優(yōu)可能會促使你對映射進行重構)。現(xiàn)在,讓我們從簡單事物開始。圖2顯示了2個模型,一個UML的類圖和一個遵循UML數(shù)據(jù)建模規(guī)則的物理數(shù)據(jù)模型。這兩張圖描繪了一個簡單的訂單系統(tǒng)。你可以看到如何映射類的屬性到數(shù)據(jù)庫的字段上。例如,圖里顯示Order類的dateFulfilled屬性映射到Order表的dateFulfilled字段,OrderItem類的numberOrdered屬性映射到OrderItem表的NumberOrdered字段。圖2.簡單映射的例子基本屬性的映射很容易確定,有幾個原因。首先,兩個模型中使用相似的命名
8、規(guī)則,這是采用敏捷建模實踐“建模標準化”的一個方面;其次,通常是同一群人創(chuàng)建這兩個模型。而當人們在不同的團隊工作的時候,很容易做出不同的方案,即便是在各個團隊本身的工作都很出色的時候也是這樣,因為他們沿著不同的方向進行決策;第三,一個模型很容易用來驅動另一個模型的開發(fā)。在“不同的項目需要不同策略”一文中我討論了當你創(chuàng)建一個新系統(tǒng)的時候,你的對象schema應該主導你的數(shù)據(jù)庫schema的開發(fā)。圖2里面顯示的兩個schema雖然很相似,但還是存在一些區(qū)別。這些區(qū)別意味著不存在一個完美的映射。2 個schema 間的不同點有:在對象schema里,tax有多個屬性而數(shù)據(jù)schema里只有一個。當對
9、象被保存時,Order類里tax的3個屬性將被相加并保存到tax字段里。然而,當對象被讀進內存時,這3個屬性將需要被計算(或者使用一個延遲初始化的方式,這樣每個屬性僅僅在第一次被訪問的時候計算)。一個像這樣的schema上的區(qū)別說明數(shù)據(jù)庫schema 需要重構,把tax字段分成3個不同的字段。數(shù)據(jù)Schema標明了鍵而對象Schema沒有。表中的每一行都有一個全表唯一的主鍵值,行間的關系被用外鍵實現(xiàn)。而對于對象之間的關系,是通過使用引用而非使用外鍵。這暗示為了完整的持久化對象和它們的關系,對象需要知道數(shù)據(jù)庫里面用來標識它們的鍵值。這些額外的信息被稱為“shadow 信息”。每個Schema里面
10、使用了不同的類型。Order里subTotalBeforeTax屬性是Currency類型,而Order 表里subTotalBeforeTax 字段是float型。當你實現(xiàn)這個映射,你需要在這些數(shù)據(jù)的表示形式之間進行無損轉換。2.1Shadow信息Shadow信息是指那些為了將對象持久化,而不得不維持的非業(yè)務數(shù)據(jù)。這通常包括主鍵信息,特別是當主鍵是沒有業(yè)務含義的代理鍵值時;并發(fā)控制標識例如時間戳或者增量計數(shù)器;以及那些版本號。例如,在圖2你可以看到Order表有一個OrderID的字段作為主鍵,一個Order類所沒有的LastUpdate字段被用來樂觀并發(fā)控制。為了正確持久化一個order對
11、象,就需要實現(xiàn)包含這些信息的shadow 屬性。圖3顯示一個對Order和OrderItem進行詳細設計的類模型。和圖2相比,有一些修改。首先,新的圖顯示了類需要正確持久化自己所需要的shadow 屬性。 shadow屬性是實現(xiàn)可見的,在它們的名字前面是一個空格而不是一個“”號,同時被指定了<<persistence>>進行說明(這不是一個UML標準)。第二,它顯示需要在兩個類之間實現(xiàn)聯(lián)系所需要的輔助(scaffolding)屬性。輔助屬性,例如Order里面的orderItems列表,同樣是實現(xiàn)可見的。第三,一個getTotalTax() 操作需要被加到Order 類
12、里來計算Order表中tax字段所需要的值。這是為什么我用屬性映射這個詞來代替屬性映射你所想要做的是映射一個類里面的屬性到數(shù)據(jù)庫里的字段上,有時這些屬性是通過簡單的屬性實現(xiàn)的,而其它某些時候是由一個或者多個操作所決定的。圖3.在一個類圖里包含"shadow信息"我還沒有討論的一種shadow信息是用一個boolean類型的標志來表示當前一個對象是否存在于數(shù)據(jù)庫中。這里的問題是當你把數(shù)據(jù)保存到一個關系型數(shù)據(jù)中,如果原先的對象是從數(shù)據(jù)庫中獲取出來的,你需要使用一個SQL update語句來保存數(shù)據(jù),否則應該使用SQL insert語句。一個普通的解決方法是為每個類實現(xiàn)一個isP
13、ersistent的boolean型信號標志(圖3里沒有顯示),當數(shù)據(jù)是從數(shù)據(jù)庫里面讀取的時候把它的值設置成true,如果對象是新創(chuàng)建的話則設置為false。在UML社區(qū)里面的一個通用的風格約定是在類圖里不顯示shadow信息,例如鍵值或并發(fā)標識。類似的,通常也不顯示支撐代碼。因為每個人都知道你需要做這種事情,所以何必浪費時間去顯示這些明顯的事實呢?Shadow信息不必用業(yè)務對象(business object)來實現(xiàn),不過那樣你的程序就要在其他地方處理這個問題。例如,在Enterprise JavaBeans (EJBs)里你把主鍵保存在EJB以外的主鍵類(primarykeyclass)里
14、,獨立對象引用相關的主鍵對象。而進一步的,JavaDataObject(JDO)則是在JDOs里面實現(xiàn)shadow 信息,而不是在業(yè)務對象(business object)里。2.2映射元數(shù)據(jù)圖4顯示了元數(shù)據(jù)(meta data),這些是代表持久化圖3里Order和OrderItem 類所需要的屬性映射。元數(shù)據(jù)是關于數(shù)據(jù)的信息。因如下原因使得圖4顯得很重要的。首先,我們需要某個方式來表現(xiàn)映射。我們可以把2個schema并排放在一起,就像圖2里面那樣,然后在它們之間畫線,但是這很快會變的非常復雜,而另外一個選擇是像圖4里面這樣列表;第二,映射元數(shù)據(jù)的概念對持久化框架的功能是非常重要的,這是一個可
15、以讓敏捷數(shù)據(jù)庫技術發(fā)揮作用的數(shù)據(jù)庫封裝策略。圖4.代表屬性映射的元數(shù)據(jù)PropertyColumnOrder.orderIDOrder.OrderIDOrder.dateOrderedOrder.DateOrderedOrder.dateFulfilledOrder.DateFulfilledOrder.getTotalTax()Order.TaxOrder.subtotalBeforeTaxOrder.SubtotalBeforeTaxOrder.shipTo.personIDOrder.ShipToContactIDOrder.billTo.personIDOrder.BillToCont
16、actIDOrder.lastUpdateOrder.LastUpdateOrderItem.orderedOrderItem.OrderIDOrder.orderItems.position(orderItem)OrderItem.ItemSequenceOrderItem.item.numberOrderItem.ItemNoOrderItem.numberOrderedOrderItem.NumberOrderedOrderItem.lastUpdateOrderItem.LastUpdate我采用的命名規(guī)則是非常直接了當?shù)模篛rder.dateOrdered指得是Order類里的dat
17、eOrdered屬性。類似的還有,Order.DateOrdered指得是Order表里的DateOrdered字段。Order.getTotalTax()指得是Order里的getTotalTax()操作而則是被Order.billTo屬性引用的Person對象里的personID屬性??雌饋碜铍y理解的屬性是Order.orderItems.position(orderItem),它指向在將要保存的OrderItem實例里Order.orderItems列表中的位置。圖4暗示了面向對象技術和關系型技術之間一個最重要的阻抗失配。Class同時實現(xiàn)行為和數(shù)據(jù),而關系型數(shù)據(jù)庫的表僅僅保存數(shù)據(jù)而已。
18、這導致當你映射一個類的屬性到關系型數(shù)據(jù)庫時,你也需要映射那些操作到數(shù)據(jù)庫字段上,例如:getTotalTax()和position()。雖然在這個例子里面沒有出現(xiàn),但是你常常需要映射僅代表一個屬性兩個操作(operation)到一個字段一個操作是設置值如:setFirstName(),而另外一個是獲取值,如getFirstName().這些操作通常分別被稱作setter和getter,或者mutator和accessor.無論何時,一個鍵值都要被映射到類里的一個屬性上,例如在OrderItem.ItemSequence和Order.orderItems.position(orderItem)之
19、間的映射,這實際是關系映射的一部分工作,將在本章的后面進行討論。這是因為在關系數(shù)據(jù)庫里通過使用鍵值來實現(xiàn)數(shù)據(jù)間的聯(lián)系。2.3如何使映射適合全過程參看文章。3.繼承結構的映射由于關系數(shù)據(jù)庫不是生來就支持繼承的,這就強制你必須將對象schema的繼承結構映射到相應的數(shù)據(jù)庫schema中去。多半是因為不牢靠的基類(譯注:不牢靠的基類是指,有時基類很難修改,因為一旦修改基類,子類就容易出錯)的原因,面向對象社區(qū)不太提倡使用繼承,而我的經(jīng)驗表明:之所以出現(xiàn)這個問題,是因為面向對象的開發(fā)者們大多缺乏封裝的技巧,而非繼承概念本身出了問題(Ambler 2001a)。我想說的是,事實上,你只需要做少量的工作,
20、即可將一個繼承層次映射到關系數(shù)據(jù)庫中去,而這并不會影響你在合適的地方運用繼承。把對象存入關系數(shù)據(jù)庫時,繼承的概念會帶來一些有趣的變化。如何在數(shù)據(jù)模型中組織那些通過繼承而得到的屬性?本節(jié)中,你將看到用來解決如何將繼承關系映射到數(shù)據(jù)庫的三種基本方法,以及第四種補充方法,它不限于繼承映射。這些方法如下所示:整個類層次結構映射到一張表每個具體類單獨映射到一張表每個類單獨映射到一張表所有類映射到一個通用的表結構如圖6 所示,有兩個版本的類層次結構,我們將通過討論如何映射這兩個結構,來深入了解每種方法。第一個版本描述了三個類:一個抽象類Person,和兩個具體類Employee 和Customer。之所以
21、知道Person 是抽象類,是因為在圖中它用斜體表示。在較早版本的UML 中,會用約束“abstract”來表示抽象類。第二個版本在第一個版本的基礎上,往類層次結構中添加了一個新的具體類Executive。旨在描述,當實現(xiàn)了第一個類層次結構之后,有了一個新的需求,要求為雇員中的執(zhí)行主管,而非普通雇員,頒發(fā)固定的年度分紅。類Executive就是為了滿足這一新功能而添加的。簡單起見,我沒有對這些類的所有屬性、屬性的完整簽名,以及類的任何操作進行建模。而這幅類圖恰好足以滿足我的目的,換句話說,這是一個敏捷的模型。此外,這些類層次結構本應該用分析模式中的Party模式(Fowler 1997)或Bu
22、siness Entity模式(Ambler 1997)。我并沒有這么做,因為在這里,我并不是為了說明分析模式的有效應用,而是用一個簡單的例子來說明繼承層次結構的映射我總是遵循敏捷建模(AM)的“每次只針對一個目標建?!保∕odelWithAPurpose)的原則。圖6. 一個簡單類層次結構的兩個版本誤用繼承也會帶來問題比如,圖11.6的層次結構本應該通過Party(Hay 1996,Fowler1997)模式或Business Entity(Ambler 1997)模式進行更好的建模。舉例來說,有人可能既是雇員又是顧客,為此你要在內存中保留多個對象,而這可能會給你的應用程序帶來問題。我選擇這
23、個例子,是因為我需要一個簡單的,易于理解的類層次結構來進行映射。3.1整個層次結構映射到一張表按照此策略,把所有類的所有屬性都存儲到一張表中去。當采用這種方法時,圖6中的類層次結構對應的數(shù)據(jù)模型如圖7所示。這是非常直觀的方式,每個類的屬性都存儲到表Person中,表名最好用類層次結構中根類的名字來命名。圖7.映射到一張表表里多加了兩個字段PersonPOID和PersonType。圖中,衍型(stereotype)<<PK>>說明第一個字段是表的主鍵,第二個字段是標識代碼,用來指明一個人是顧客還是雇員,抑或兩者皆是。PersonPOID是一個代理鍵(surrogatek
24、ey),它是持久化對象的標識(POID,persistentobjectidentifier),通常簡稱為對象標識(OID,object identifier)。本應該使用可選衍型(stereotype)<<Surrogate>>來標示的,但POID已經(jīng)暗示了這層意思,這表明,這類衍型(stereotype)只會無謂地使我們的類圖更復雜(參見AM實踐“簡單地描述模型”(Depict Models Simply))。數(shù)據(jù)建模101()詳細討論了代理鍵(surrogate keys)的相關內容。用來識別對象類型的字段PersonType是必需的,這個對象可以由給定的數(shù)據(jù)庫中
25、的一行數(shù)據(jù)實例化而來。例如,取值為E表示該人是雇員,C表示是顧客,B則表示既是雇員又是顧客。這種方法看似直觀,但當類型數(shù)目和類型間聯(lián)合越來越多的時候,這種方法就漸漸變的力不從心了。例如,添加執(zhí)行主管的概念,需要添加一個碼值,比如以X來代表。對于值B來說,它代表的是既是雇員又是顧客,此時就顯得有點不倫不類了。此外,有些(類間)聯(lián)合中可能會包括執(zhí)行主管,比如,一個人既是執(zhí)行主管又是顧客也是很合乎情理的事情,這種情形也需要一個碼值來表示。對于各種類型聯(lián)合的情況,應該考慮使用“用布爾值來代替類型碼”(ReplaceTypeCodeWithBooleans)的數(shù)據(jù)庫重構技法,如圖8所示。為了簡單,沒有包
26、含那些需要并發(fā)控制的字段,比如位于圖3表中的時間戳字段,同時,用于數(shù)據(jù)版本跟蹤的字段也沒有包括在內。圖8.一種重構方法3.2每個具體類映射到單獨的一張表這種方法為每個具體類創(chuàng)建一張表,每張表既包括對應類自己實現(xiàn)的那些屬性,也包括它繼承下來的那些屬性。采用這種映射方法,圖6中的類層次結構所對應的數(shù)據(jù)庫物理數(shù)據(jù)模型如圖9所示。每個具體類Customer和Employee都有對應的映射表,對象從這些表中被實例化,而抽象類Person則沒有對應的表。分別為每張表分配了對應的主鍵,customerPOID和employeePOID。為了支持后加的類Executive,需要做的全部事情就是添加一張相應的表
27、,表中包括所有Executive對象所需要的屬性。圖9.把具體類映射成表3.3每個類單獨映射到一張表遵循這個策略,為每個類創(chuàng)建一張表,每個業(yè)務屬性和任何必須的標識信息都對應于表中的一個字段(還包括并發(fā)控制和版本跟蹤所需的其他字段)。將每個類都映射成一張單獨的表,圖6中的類層次結構對應的物理數(shù)據(jù)模型如圖10所示。類Customer的數(shù)據(jù)被存儲在兩張表Customer和Person中,因此要獲取這些數(shù)據(jù),你需要連接這兩張表(或者分兩次讀取,每張表讀一次)。鍵的應用很有意思。注意personPOID是如何作為所有表的主鍵來使用的。對于Customer,Employee,和Executive這些表而言
28、,personPOID既是主鍵又是外鍵。對于Customer表,personPOID是它的主鍵,同時也作為外鍵來維系與表Person之間的關聯(lián)。這是用<<PK>>和 <<FK>>兩種衍型(stereotype)來表示的。在一些較早版本的UML中,不允許為單個模型元素賦多個衍型(stereotype),但在UML1.4版中,取消了這一限制。圖10.每個類單獨映射一張表通常你可能會考慮的修改,是往表Person中添加一個類型字段或者布爾字段,用來表示person的可用子類。這個額外的花銷將使一些查詢變的更容易。在很多情況下,添加額外的視圖(view)
29、也是可行的選擇,我更傾向于這種方法,因為與附加類型或布爾字段相比,這更易于維護。3.4將類映射為一個通用的表結構將繼承結構映射到關系數(shù)據(jù)庫中去的第四種選擇是采用一種通用的,有時被稱為元數(shù)據(jù)驅動(meta-datadriven)的方法來映射你的類,這種方法并不局限于繼承結構,它支持所有形式的映射。圖11中所示的,是用于存儲屬性值和遍歷繼承結構的數(shù)據(jù)schema。這個schema并不完全,例如它還可以被擴展,用來映射關聯(lián)關系,但對于我們的目的而言是足夠用了。單個屬性的值存放在Value表中,因此,如果要保存一個帶有十個業(yè)務屬性的對象,那么需要十條記錄,每個屬性對應一條記錄。字段Value.Obje
30、ctPOID用來存儲特定對象的唯一標識(這種方法假定對所有對象采用統(tǒng)一的鍵生成策略,假若不是這樣的話,你必須適當?shù)臄U展這個表。) 表AttributeType包含了代表基本數(shù)據(jù)類型的記錄,如數(shù)據(jù),字符串,錢款,整數(shù)等等。將對象的屬性值轉換成為varchar類型保存到Value.Value字段中時,會需要這一信息。圖11.一個用于存儲對象的通用數(shù)據(jù)schema讓我們一起來看一個例子:將單個類映射到這種schema中。若要存儲圖3中的類OrderItem,表Value中要有三條記錄,一條存儲已訂購的訂單項的數(shù)目,一條存儲OrderPOID的值,該訂單項是相應訂單的一部分,還有一條存儲ItemPOI
31、D的值,這個值用來描述訂單項。如果你采用樂觀鎖的方法進行并發(fā)控制,你也可以考慮用第四條記錄來儲存lastUpdated這個shadow屬性。表Class將為類OrderItem創(chuàng)建一行記錄,表Attribute將在數(shù)據(jù)庫中為每個屬性創(chuàng)建一行記錄(在本例子中,有三行或四行記錄)現(xiàn)在,將圖6所示的Person和Customer之間的繼承結構映射到這種schema中。表Inheritance是繼承映射的關鍵。每個類將由表Class中的一行來表示。在表Inheritance中也有一行,Inheritance.SuperClassPOID的值指向表Class中表示類Person的行,Inheritanc
32、e.SubClassPOID的值指向表中表示類Customer 的行。映射剩余的結構層次關系,需要在Inheritance表中為每個繼承關系都添加一行。3.5多重繼承映射到目前為止,我所關注的是單根繼承層次結構的話題,所謂的單根繼承是指,子類,如Customer,直接繼承單一的父類,如Person。多重繼承是指一個子類有兩個或兩個以上的直接父類,如圖12所示,Dragon直接繼承了類Bird和Lizard。在面向對象語言中,多重繼承通常被認為是有問題的,自1990年以來,我只見過在一個領域問題中使用多重繼承是有意義的,因此,多數(shù)語言不支持多重繼承。但是像C+和Eiffel這樣的語言支持多重繼承
33、,所以也存在需要將多重繼承層次結構映射到關系數(shù)據(jù)庫中去的情況。圖12展示的是,對多重繼承分別使用三種繼承映射策略而得到的結果數(shù)據(jù) schema。如圖所示,多重繼承映射也是很簡單的,跟單根繼承映射相比,并沒有任何特別的東西。我所經(jīng)歷的最大挑戰(zhàn)是,當將整個層次結構映射到同一張表中時,如何為這張表取一個合理的名字,在本例中,用Creature最合適。圖12.映射多重繼承3.6映射策略之間的比較如表1所示,就這些策略而言,沒有一種策略對所有場合都是理想的。我的經(jīng)驗表明,最容易奏效的策略是,先用每個層次結構映射一張表的策略,接下來如果需要,就重構相應的schema。有時,當我的團隊被“純設計方法”的工作
34、方式所驅動,我會先采用每個類映射一張表的策略。我盡量不使用每個具體類映射一張表的策略,因為這樣做最后導致的典型結果是,需要把數(shù)據(jù)在表之間拷來拷去的,這就強制我在項目的初期就要對它進行合理重構。我很少使用通用schema 方法,很簡單,因為它不具有很好的可伸縮型。在任何應用程序中,你都可以聯(lián)合使用前三個映射策略:每個層次結構一個張表、每個具體類一張表和每個類一張表, 理解這一點很重要。你甚至可以在一個大型的層次結構中聯(lián)合使用這三種策略。表1.繼承映射策略的比較Strategy策略Advantages優(yōu)勢Disadvantages缺陷When to Use使用的時機每個層次結構一張表方法簡單。添加
35、新類很方便,你只需要為新加的數(shù)據(jù)添加新的字段即可。通過簡單的修改行的“類型”字段來實現(xiàn)多態(tài)。因為數(shù)據(jù)在一張表中,因此數(shù)據(jù)的訪問速度很快。因為所有的數(shù)據(jù)在同一張表中,特別容易生成專門的報表。因為所有的類直接關聯(lián)到同一張表,類層次結構內的耦合度增加。修改一個類將影響整張表,而整張表的改變又會影響類層次結構中的其他類。數(shù)據(jù)庫空間存在潛在浪費。如果已有類型之間有較大的重疊,則暗示著類型復雜化了。對大的層次結構而言,表會非常大。對那些類層次結構內部的類型間不存在或存在極少重疊情況的簡單類層次結構和(或)繼承深度比較淺的類層次結構而言,這種策略很好每個具體類一張表由于單個類的所有數(shù)據(jù)都存儲在一張表中,所以
36、特別容易生成專門的報表。訪問單個對象數(shù)據(jù)的性能高。類修改的時候必須修改它對應的表,同時也必須修改它所有的子類對應的表。舉例來說,如果你給類Person 添加了height和weight兩個屬性,那么你需要給表Customer,Employee,和Executive 添加對應的字段。當一個對象改變了它的角色,比如,你雇用了你的顧客,你需要把對象的數(shù)據(jù)復制到相應的表中,并為它分配一個新的POID 值(或者可以重用已有的POID值)。很難既支持多角色又能保持數(shù)據(jù)的完整性。比如,你把既是顧客又是雇員的人的名字存放到哪里?當類型很少改變,類型之間極少重疊時,可以選擇使用這種策略。每個類一張因為是一一對應
37、的映射,每個類一張表,則在數(shù)據(jù)庫在類型之間有較大表所以容易理解。每個類型的記錄分別在相應的表中,因此可以很好的支持多態(tài)。修改父類,或添加新的子類時,只需要簡單的修改或添加一張表。數(shù)據(jù)的大小跟對象個數(shù)的增長成正比。中有很多表(還需要附加表用來維護類之間的關系)。使用這種方法,使得讀寫數(shù)據(jù)需要更長的時間,因為需要訪問多張表。如果你明智的組織你的數(shù)據(jù)庫,把一個類層次結構中的每張表放入不同的物理驅動盤盤面上(假設驅動器磁頭的所有操作相互獨立),就可以提高數(shù)據(jù)的讀寫速度。從數(shù)據(jù)庫生成專門的報表特別困難,除非添加視圖來模擬所需要的表。重疊,或類型會頻繁的修改的情況下使用這種策略。通用schema當用一個穩(wěn)
38、定的持久化框架來封裝數(shù)據(jù)庫的訪問,可以工作的非常好??梢詳U展到提供元數(shù)據(jù),以支持包括關系映射在內的更大范圍的映射。簡而言之,它是元數(shù)據(jù)映射引擎的起點。非常靈活,可以快速的改變存儲對象的方式,因為只需要更新存儲在表Class ,Inheritance,Attribute和AttributeType 中的元數(shù)據(jù)。很先進的技術最初可能難以實現(xiàn)。使用這種方法,需要訪問很多數(shù)據(jù)庫的記錄來創(chuàng)建一個對象,因此它只適用于少量數(shù)據(jù)的情況。你可能需要一個小型的管理程序來維護元數(shù)據(jù)。因為要訪問多行記錄來獲取一個對象的數(shù)據(jù),因此,用這種數(shù)據(jù)生成報表顯得異常困難。適用于如下場合:處理少量數(shù)據(jù)的復雜應用程序;不常訪問數(shù)據(jù)
39、的應用程序;可以預先將數(shù)據(jù)讀入到緩存的應用程序。4.映射對象關系除了屬性映射與繼承映射,你還需要領會關系映射的藝術。有三種你需要進行映射的對象間關系:關聯(lián)、聚合以及組合。在這里,我將把這三種關系同等看待盡管涉及到引用完整性的時候,三者有些微妙差別,但他們的映射方式是一樣的。4.1.關系的類型在做映射時,你需要關心兩類對象關系。第一類是基于多重性(multiplicity)的,包含三種類型:一對一關系(One-to-one relationships)。這是一種兩端多重性(multiplicity)最大值都為1的關系(譯注:兩端最多只有一個對象) 。舉個例子來說就像圖13中Employee與Po
40、sition之間的擁有(holds)關系。每個雇員擁有且僅擁有一個職位,每個職位可能擁有一個雇員(有些職位還可能空缺)。一對多關系(One-to-many relationships)。也被稱作多對一關系(many-to-one relationship),這種關系產(chǎn)生于一端多重性(multiplicity)最大為1,而另一端大于1的場合。例如Employee與Division之間的隸屬(workin)關系。每個雇員在一個部門工作,任何給定部門都有一個或者多個雇員在里面工作。多對多關系(Many-to-many relationships)。這是一種兩端多重性(multiplicity)最大值
41、均大于1的關系。例如Employee與Task之間的分派(assigned)關系。每個雇員可以被分派一個或多個任務,每個任務可以被指派給0個或多個雇員。第二類是基于方向(directionality)的,包含兩種類型:單向關系和雙向關系。單向關系(Uni-directionalrelationships)。單向關系是指一個對象知道與其關聯(lián)的其他對象,但是其他對象不知道該對象。例如,圖13中,Employee和Position 之間的擁有(holds)關系,圖中該關系是用帶開口箭頭的直線來表示的。Employee 對象知道其所擁有的職位,而Position對象不知道擁有它的雇員是誰(沒有需要知道
42、的必要)。不久你就會看到,單向關系要比雙向關系容易實現(xiàn)。雙向關系(Bi-directionalrelatinships)。雙向關系是指,關聯(lián)兩端的對象都彼此知道對方。例如Employee與Division之間的隸屬(workin)關系。Employee對象知道自己工作的部門,而Division對象也知道有哪些雇員在本部門工作。圖13.對象間的關系對象schema中有可能包含全部六種關系的組合。然而,關系技術并不支持單向關系的概念在關系數(shù)據(jù)庫中,所有的關聯(lián)都是雙向的,這也是對象技術與關系技術之間阻抗失配(impedance mismatch)的一個方面。4.2.如何實現(xiàn)對象關系對象schema中
43、的關系是通過對象的引用及操作來實現(xiàn)的。當多重性 (multiplicity)是1(比如0.1或者1)的時候,這種關系通過一個對象引用、一個getter操作以及一個setter操作來實現(xiàn)。舉例來說,圖13中,類Employee 是通過組合division屬性、返回division屬性值的getDivision()操作以及設置division屬性值的setDivision()操作來反映某個雇員隸屬于某一部門這個事實的。用來實現(xiàn)對象關系的屬性和操作通常稱為輔助屬性和輔助操作。多重性(multiplicity)為“多”(比如 N,0.*,1.*)的關系是通過集合屬性 (collection attri
44、bute)(比如Java中的Array或者HashSet),以及操縱該集合的操作來實現(xiàn)的。例如,類Division定義了名叫employees的HashSet屬性、用來取值的getEmployees()、用來設值的setEmployees()、將一個雇員對象 (employee)加入到HashSet的addEmployee()以及從HashSet中刪除雇員對象(employee)的removeEmployee()。關系是單向的時候,只需要在“知道其它對象”的對象中實現(xiàn)代碼即可。比如Employee與Position間的單向關系,只需由類Employee來實現(xiàn)關聯(lián)。另一方面,雙向關聯(lián)則需要兩個類
45、都實現(xiàn)相關代碼,正如你在Employee與Task之間的多對多關系(many-to-many relationship)中看到的那樣。4.3.如何實現(xiàn)關系數(shù)據(jù)庫中的關系關系數(shù)據(jù)庫中,關系是通過使用外鍵(foreign key)來維護的。外鍵的值可能是另一個表(某個行)鍵值的一部分,也可能就等于那個表(某個行)的鍵值。一對一關系中需要其中一張表來定義外鍵。在圖14中你可以看到表Position包含了EmployeePOID,它是一個指向Employee表的外鍵,用來實現(xiàn)兩者的關聯(lián)。換種方式,在Employee表中定義PositionPOID字段,也很簡單。圖14.關系數(shù)據(jù)庫中的關系要實現(xiàn)一對多關
46、系,你需要定義一個從"一表"指向"多表"的外鍵(譯注:在one-to-many關系中,UML里所指的one和many和數(shù)據(jù)庫里的multiplicity是反過來的。比如一個division有幾個employee,UML里認為division是one端,因為一個division對應幾個employee;而數(shù)據(jù)庫Entity-Relationship Diagram 里的one端指的是employee,因為一個employee只會參與一個division-employee關系,這個值就是所謂的multiplicity)。例如Employee(多方)中就包含一
47、個DivisionPOID字段(外鍵),用于實現(xiàn)與Division(一方)的隸屬(workin)關系(注:同時,字段DivisionPOID在Division中是主鍵)。你也可以選擇增設一個關聯(lián)表(associative table)來實現(xiàn)一對多關系(one-to-many relationship),從而很容易實現(xiàn)多對多關系(many-to-many)。在關系數(shù)據(jù)庫中,有兩種方式可以用來實現(xiàn)多對多關聯(lián)(many-to-many association)。第一種是在每個表中定義多個指向其他表的外鍵。比如要實現(xiàn)Employee與Task之間的多對多關系,你可以在Employee表中定義五個Tas
48、kPOID 字段,在Task表中定義七個EmployeePOID字段。不過當你想要賦予一個雇員超過五項任務,或者一項任務被分配給超過七個雇員時,采用這種方法就會遇到麻煩。更好的方式是實現(xiàn)一張被稱為的關聯(lián)表(associative table)的表。正如圖14中的EmployeeTask所示,關聯(lián)表包含了與其相關聯(lián)的表的主鍵的組合。采用這種方式,你可以將同一項任務分配給五十個人,或者給同一個人指派二十項任務,完全沒有問題。這個基本“技巧”的要點在于將多對多關系轉化為兩個一對多關系,它們都與同一個關聯(lián)表相關聯(lián)。因為外鍵是用來連接表的,所以關系型數(shù)據(jù)庫中的所有關系都是雙向的。這也就是為什么你在哪張表
49、中實現(xiàn)一對一關系都無所謂的原因,連接兩表的代碼相差無幾。比如,圖 14中,對于現(xiàn)有的schema,連接兩者(Position和Employee)之間的holds關系所使用的SQL代碼應為SELECT * FROM Position, EmployeeWHERE Position.EmployeePOID = Employee.EmployeePOID假如在Employee表中實現(xiàn)外鍵,SQL代碼應為SELECT * FROM Position, EmployeeWHERE Position.PositionPOID = Employee.PositionPOID在數(shù)據(jù)庫中采用一致的鍵策略可以極
50、大地簡化進行關系映射所需的努力。首先是盡可能使用單字段的鍵。然后是采用某種全局唯一的代理鍵(surrogate key),可以遵從GUID或者HIGH-LOW生成策略。這樣的好處是你只需對同一種類型的鍵值列進行映射。既然我們明白了如何分別在兩種技術(譯注:對象技術與關系數(shù)據(jù)庫技術)中實現(xiàn)關系,那么我們來看看怎么對它們做映射。我會從對象關系到關系數(shù)據(jù)庫映射的角度來講解。記住一件事情,某些情況下你要做出設計決策。再之,謹防“神奇的CASE工具欄按鈕”,不要幻想它們會為你包辦一切。4.4.關系映射關系映射的一條通用的經(jīng)驗規(guī)則是你應該使映射前后的多重性保持一致。因此一對一的對象關系映射成一對一的數(shù)據(jù)關
51、系,一對多的映射成一對多的,多對多的映射成多對多的。不過世事無絕對,你也可以將一對一關系映射成一對多甚至多對多關系。這是因為一對一關系是一對多關系的子集,一對多關系也同樣是多對多關系的子集。圖15 描述了圖13 的對象schema與圖14 的數(shù)據(jù)庫schema 之間的屬性映射。注意我只需要映射對象的業(yè)務屬性和shadow信息,不需要映射像Employee.position 和Employee.tasks 之類的輔助屬性(scaffoldingattribute)。這些輔助屬性是通過映射到數(shù)據(jù)庫中的shadow信息來表示的。當關系信息被讀入內存,儲存于主鍵字段中的值就會被解釋成對象中相應的sha
52、dow屬性。與此同時,通過為相關對象的輔助屬性設置適當?shù)娜≈?,主鍵字段所表達的關系也會被建立起來。圖15.屬性映射PropertyColumnPosition.titlePosition.TitlePosition.positionPOIDPosition.PositionPOIDEEmployee.NameEmployee.employeePOIDEmployee.EmployeePOIDEmployee.employeePOIDEmployeeTask.EmployeePOIDDDivision.NameDivision.divisionPO
53、IDDivision.DivisionPOIDTask.descriptionTask.DescriptionTask.taskPOIDTask.TaskPOIDTask.taskPOIDEmployeeTask.TaskPOID4.4.1一對一映射考察Employee與Position之間一對一的對象關系。我們可以采取無論何時,只要一個Position對象或者一個Employee對象被讀入內存,應用程序都會自動遍歷擁有(holds)關系并自動將關聯(lián)對象也讀入內存的方案。另一個可供選擇的方案是在代碼中手工遍歷擁有關系,采用延遲讀取方式,即:在應用程序需要的時候才讀入另一個對象。兩種方案的權衡在
54、實現(xiàn)引用完整性中進行了討論。圖16顯示了對象關系是如何被映射的。圖16.映射關系對象關系從到基數(shù)自動讀取列框架屬性擁有(holds)EmployeePositi onOneYesPosition.EmployeePOI DEmployee.positio n被擁有(heldby)PositionEmpl oyeeOneYesPosition.EmployeePOI DEmployee.positio n隸屬(worksin)EmployeeDivis ionOneYesEmployee.DivisionPOI DEmployee.divisio n屬下有(hasworkingin it)DivisionEmpl oyeeManyNoEmployee.DivisionPOI DDivision.employe es被賦予(assigned)EmployeeTaskManyNoEmployee.Employee
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
- 4. 未經(jīng)權益所有人同意不得將文件中的內容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
- 6. 下載文件中如有侵權或不適當內容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 26《好的故事》說課稿-2024-2025學年語文六年級上冊統(tǒng)編版
- 1場景歌說課稿-2024-2025學年統(tǒng)編版語文二年級上冊
- 2024年秋一年級道德與法治下冊 第二單元 我和大自然 5 風兒輕輕吹說課稿 新人教版
- 18古詩三首浪淘沙(其一)說課稿-2024-2025學年六年級上冊語文統(tǒng)編版
- 8 設計制作小車(二) 說課稿-2024-2025學年科學四年級上冊教科版
- 23《月光曲》說課稿-2024-2025學年語文六年級上冊統(tǒng)編版
- 1 24時計時法(說課稿)-2024-2025學年三年級上冊數(shù)學人教版001
- 2023九年級道德與法治上冊 第三單元 文明與家園 第五課 守望精神家園第2框 凝聚價值追求說課稿 新人教版
- 2025北京市飼料采購合同新
- 2025建造船舶所要用到的合同
- 煙葉復烤能源管理
- 食品安全管理員考試題庫298題(含標準答案)
- 執(zhí)業(yè)醫(yī)師資格考試《臨床執(zhí)業(yè)醫(yī)師》 考前 押題試卷絕密1 答案
- 2024年山東濟寧初中學業(yè)水平考試地理試卷真題(含答案詳解)
- 社會保險課件教學課件
- 訂婚協(xié)議書手寫模板攻略
- 準備單元 雪地上的“足跡”(教學設計)-2023-2024學年五年級下冊科學大象版
- 宇航用商業(yè)現(xiàn)貨(COTS)器件保證指南-編制說明
- 音樂學科閱讀方案
- 《立體倉庫鋼結構貨架技術規(guī)范(征求意見稿)》
- 2024年貴州蔬菜集團有限公司招聘筆試參考題庫附帶答案詳解
評論
0/150
提交評論