1. 介紹
許多人認為物件導向概念和關係型資料庫相互不一致,並且不能結合。事實上完全相反!經過靈活的使用,一個關係型資料庫能夠為物件導向(OO)模型提供一套優秀的實現。同樣的模型能夠用來開發編程代碼和關係型資料庫結構。
關係型資料庫技術是意義深遠的、強大的,但它比許多開發商使你相信的要難得多。單個表是簡單易懂的、直觀的。但由數以百計的表組成(這是常見的)的應用要徹底瞭解是相當困難的。這正是OO模型有用之處。 OO模型使你深入地、連貫地思考問題。
OO模型提供一種問題的超結構(superstructure)的思考方式,然後該方式能夠用關係型資料庫的更低層的組成塊來實現。
本文章綜合地討論了關係型資料庫技術,而不是集中於特定的產品上。我們將不討論實體設計細節(例如儲存分配和物理聚集),因為它們是依賴於產品的。
用關係型資料庫實現UML模型有兩個方面:映射結構(第2節)和映射功能(第3節)。第4節註解了物件導向到關係型資料庫的擴充。第5節總結本文章。
2. 結構映射到表
UML物件模型在本質上只是一個擴充的實體-關係(ER)模型 。使用設計資料庫的ER模型的方式受到普遍接受,而我們以一種近似的但更強大的方式-使用UML物件模型。OO模型的主要優勢在於編程和資料庫的相同的模型工作。而且,作為考慮功能性的一種方式(第3節),我們強調OO模型的導航。這一節顯示如何?UML物件模型的主要構造。
2.1 標識(identity)
實現物件模型的第一步是處理標識。我們從定義幾個術語開始。
1)候選索引鍵(candidate key)是一個或多個屬性的組合,它唯一地確定某個表裡的記錄。一個候選索引鍵裡的屬性集必須是最小化的;除非破壞唯一性,否則屬性不能從候選索引鍵刪除。候選索引鍵裡的屬性不可為空。
2)主鍵(primary key)是一個特定地選定的候選索引鍵,用來優先地參考記錄。
3)外鍵(foreign key)是一個候選索引鍵的參考。外鍵必須包括每個要素屬性的一個值,或者它必須全部為空白。外鍵用來實現關聯和一般化。
正常地你應該為每個表定義一個主鍵,儘管偶爾有例外。我們強烈建議所有的外鍵都只指向主鍵而不是其它的候選索引鍵。
定義主鍵有兩種基本的方法:
1)基於存在的標識。你應該為每個類表加一個物件識別碼屬性,並將它設為主鍵。每個關聯表的主鍵包括一個或更多的相關類的標識符。基於存在的標識符有作為單獨屬性的優勢,佔位小且大小相同。只要你的關係型資料庫管理系統(RDBMS)受支援,基於存在的標識符就沒有效能的劣勢。(多數RDBMS提供有效基於存在的標識符的分配順序號碼。)唯一的劣勢是基於存在的標識符在維護時內沒有固有的意義。
2)基於值的標識。一些真實世界的屬性的組合確定了每個對象。基於值的標識有不同的優勢。主鍵對於使用者有固有的意義,容易進行調試和資料庫維護。在另一面,基於值的主鍵很難改變。一個主鍵的改變需要傳播到許多外鍵。一些對象沒有自然的真實世界裡的標識符。
我們推薦你在超過30個類的RDBMS應用裡使用基於存在的標識。基於存在和基於值的標識都是所有RDBMS應用的可行選項。
2.2 域(屬性類型)
屬性類型是UML術語,對應於資料庫著作裡的域的術語。比起直接用資料類型,域提升到更一致的設計,並便利了應用的定位。
簡單域很容易實現。你僅僅要定義相應的資料類型和大小。並且每個用了域的屬性,你都必須為每個域約束加入一條SQL查詢子句。簡單域的一些例子是:名字(name),長字元(longString)和電話號碼(phone-Number)。
一個列舉欄位把一個屬性限制在一系列的值裡。列舉欄位比簡單域實現起來更複雜,圖表1顯示了四個方法。
圖表1:枚舉的實現方法
2.3類
正常情況下,我們把每個類映射為一個表,每個屬性對應為一個列。你可能因一個已產生的標識符(基於存在的標識符)、隱藏的關聯(第2.4節)和通用鑒別器(第2.5節)需要一些另外的列。
2.4關聯
現在我們討論關聯的實現。我們已經把我們的陳述分為建議的映射(我們正常使用的映射),可選的映射(我們偶爾使用的映射)和不鼓勵的映射(我們遇到的應該避免的錯誤)。我們所有的例子都採用基於存在的標識。
2.4.1 建議的映射
多對多關聯。用一個特別的表(圖表2)來實現一個多對多關聯。關聯的主鍵是每個類的主鍵的合并。那些省略符號(...)表示在模型裡沒有顯示出來的屬性。主鍵用黑體字型顯示。
一對多關聯。把一個外鍵隱藏在“多”表(圖表3)。角色名稱字成為外鍵屬性名稱字的一部分。
零或一對一關聯。把外鍵隱藏在“零或一”表(圖表4)。
其它一對一關聯。把外鍵隱藏在任一表裡。
圖表2:建議的實現:特殊的多對多關聯表
圖表3:建議的實現:隱藏的一對多關聯
圖表4:建議的實現:隱藏的零或一對一關聯
可選的映射 正常情況下我們使用建議的映射。但有些偶爾的情況,可選的映射更合適。
特別的表。你也可以用特別的表(圖表5)來實現一對多和一對一關聯。特別的表給了你更統一的設計和更大的擴充性。無論如何,特別的關聯表打碎了資料庫,並增加了表的數量。此外,特別的關聯表不能強迫一個更低的多重性限度為“一”。
圖表5:可選的實現:特別的一對x關聯表
不鼓勵的映射 我們已經注意到有些開發人員選擇有缺陷的映射。我們要注意這些映射以便可以避免。 合并。不要合并多個類,不要把關聯強製成為一個單獨的表(圖表6)。這樣減少了表的數量,但會干擾第三範式。
兩次隱藏一對一關聯。不要把一個一對一關聯隱藏兩次,每次隱藏在一個類裡(圖表7)。這是多餘的,無助於效能。
相同的屬性。不要用相同的屬性來實現多個關聯角色(圖表8)。相同的屬性使編程複雜,降低了擴充性。 泛化 現在我們討論泛化。我們這裡只論述單個繼承。 建議的映射 最簡單的方法是只映射超類和每個子類為一個表。所有的表共用一個共同的主鍵。應用必須執行子類的劃分,因為RDBMS支援。(關於後者的詳盡的描述,請參閱第4節。)
特別的表。映射超類和每個子類為一個表(圖表9)。所有的表共用一個共同的主鍵。鑒別器指出每個子類記錄的適當的超類表。
圖表9:建議的實現:分開的超類和子類表
可選的映射 泛化有幾個可選的映射。 消除。你可以最佳化除去那些除了主鍵外沒有別的屬性的類(圖表10)。這樣減少了表的數量,但提供更少的正規實現。
減少超類屬性。你可以除去超類表並把超類屬性複製到每個子類(圖表11)。這樣可以有描述每個對象為一個表的優勢。無論如何,它將引起資料庫結構的冗餘,你尋找一個對象時可能需要搜尋多個子類表。
增加子類屬性。作為第三個可選項,你可以除去子類表並儲存所有的子類屬性到超類表裡(圖表12)。這樣用一個表描述每個對象,但幹擾了第二範式。
圖表10:可選的實現:消除不必的子類表
圖表11:可選的實現:減少超類屬性
圖表12:可選的實現:增加子類屬性
參考完整性 一旦你已經建立了表,你就應該定義參考完整性動作來明確物件模型的意義。(不要使用SQL觸發器來實現參考完整性!)如果你使用基於存在的標識,你將不需要傳播更新的結果。我們建議以下對刪除的參考完整性方針: 泛化。級聯從泛化實現中產生的外鍵的刪除。
隱藏的關聯,最小化多樣性為零。正常地把外鍵設為空白,但有時候你可能要禁止刪除。
隱藏的關聯,最小化多樣性為空白。你可以級聯一個刪除的結果或者禁止該刪除。
關聯表。正常地我們級聯關聯表裡對記錄的刪除。可是,有時候我們禁止一個刪除。
我們已經簡要地論及參考完整性,因為它是個進階話題。參考有更多的解釋z和例子。 索引 實現資料庫結構的最後的一步是加入索引來調整資料庫效能。正常地,你應該為每個主鍵和候選索引鍵定義一個唯一的索引。(多數RDBMS作為SQL主鍵和候選索引鍵約束的副作用來建立唯一的索引。)你也應該為每個被主鍵或候選索引鍵所約束的外鍵建立一個索引。
我們強調索引的重要性。外鍵和主鍵的索引使在物件模型裡能快速地遍曆是不容懷疑的。你必須包括這些索引否則你將使使用者感到灰心。你應該在你的資料庫開始設計階段裡加入索引,因為它們很容易加入並且也沒有什麼好理由延遲加入。
資料庫管理員(DBA)可能為經常請求的查詢定義了額外的索引。DBA也可能採用產品相關的調整效能的機制。 範式 範式是關係型資料庫設計的提高資料一致性的有效方法。我們的書3討論了範式,但我們關於這個問題卻言過甚微。我們將利用這篇文章的機會來澄清我們的觀點。如果你不熟悉範式你可以跳過這節。我們的說明是針對關係型設計人員,他們正在嘗試用物件導向適應他們原有的技能。
範式是正確設計關係型資料庫的精確的原則。同樣地,它們與使用了什麼開發技術是無關的 - 基於屬性的設計、基於實體的設計、物件導向設計或其它什麼。 過去使用基於屬性設計的方法,開發人員不得不非常注意範式;範式提供了分組資料的根據。相反地,範式對於基於物件導向(或基於實體)的開發不是很重要。如果你採用OO方法並且你的模型經過很好的構思,那你就正在把資料群組織成為有意義的單位,也在本質上滿足了範式的規定。如果你願意,你仍能夠檢查範式,但這樣的檢查是不必要的。 摘要 圖表13總結了我們已經陳述的映射規則。這些映射規則的完整例子,包括一個UML物件模型,能夠在這篇完整的擴充版本裡找到(Adobe Acrobat PDF檔案)。
圖表13:推薦的映射規則的摘要
把功能映射到SQL命令 物件模型為資料庫應用提供三種主要的用途。 結構。物件模型指明資料庫結構。我們已經在第二節探討了這個方面。
約束。物件模型也指明了能儲存的資料上的重要的約束。相匹配的實現必須為迎合這些約束而努力。我們的映射規則的處理方法以及第二節裡的參考完整性指出了許多約束。(本文沒有論及的另外的UML構造,能擷取更多的約束。)
潛在估算。一個物件模型指明潛在估算;它是關於引起哪些查詢和如何公式化的藍圖。第三節將簡要地闡明第三個目的。
物件模型不僅僅是被動的資料結構,相反它們能夠協助你思考一個應用的功能。你可以根據遍曆一個物件模型說出它的許多功能。例如,根據我們對一個模型檢查用例時的遍曆,我們進行思索。這強調物件模型的估算能力對於RDBMS應用是特別重要的,因為遍曆運算式可以直接映射到SQL代碼。
UML對象約束語言(Object Constraint Language,OCL)有助於表達遍曆。點符號導航從對象到對象和對象到屬性。方括弧表示對象集合的過濾器。我們加入冒號(:)操作符來表示泛化的遍曆;因為我們正常地用多個表來實現一個泛化繼承,清楚的遍曆很有用。
圖表14裡的遍曆運算式例子是基於我們建立的UML物件模型上的(請參閱本文的擴充版本(Adobe Acrobat PDF檔案)),我們把它們映射為SQL代碼。我們用冒號開始SQL編程變數。
圖表14:物件模型遍曆和SQL代碼的例子
到RDBMS的OO擴充 資料庫團體對RDBMS的OO擴充有興趣。產品和SQL標準正嘗試加入到OO擴充裡。我們將簡要地陳述一下這個技術的方向。 抽象資料類型(ADT)。這是個好主意,擴充RDBMS的能力。開發商為這個技術使用了許多名字,例如Oracle cartridge和Informix data blades。ADT的缺點是它們把你緊緊綁在特定的一個開發商上;ADT的範疇超越了SQL標準。因此,你應該只在ADT的好處很明顯的時候才使用。
在關於ADT如何適合資料庫開發的著作裡有一些混亂。如果你使用OMT開發過程,你能夠用屬性實現簡單域,用ADT實現複雜域。你仍應該用表實作類別。 SQL3指標。最新的SQL標準的版本,SQL3,加入了作為一種資料類型的指標符號。顯然,其意圖是支援導航和物件導向。我們對於SQL3指標最友善的評語是,它們是嫁接的,是可以忽略的。更深入的批評是,指標在理論上是荒謬的,增加了複雜性,又沒有擴充SQL的表達能力。CJ Date在上次的九月對象/關係型會議上雄辯地討論了這一點。
好了,那麼我們冷淡地對待抽象資料類型和SQL指標的指責。但我們相當喜歡物件導向技術和關係型資料庫。有兩個為RDBMS的擴充可以使它們更容易用於OO技術。我們將很樂意看到RDBMS開發商把這些能力加入到他們的產品中。 擴充的參考完整性動作來支援泛化。當前的參考完整性機制是單向的。為了完全支援泛化,我們需要一個雙向的機制。這樣,一條超類記錄就可以依賴一條子類記錄。並且,一條子類記錄就可以依賴一條超類記錄。我們通過例子可以最好地解釋這點。
圖表15摘錄於我們的在3的財務案例學習。我們用資產超類來統一某些沒有顯示在摘錄裡的通用的資料和功能。一項資產可以是一隻股票或股票特權。一隻股票可以有許多它的股票特權。例如,IBM股票可以有許多達到價格和到期日期的寫下的放或叫特權。
圖表15:參考完整性和泛化的例子
我們推薦的泛化實現是特別的表 - 映射該超類和每個子類為一個表。然後,我們就可以使用參考完整性使股票特權和股票的記錄依賴於資產。一個資產記錄的刪除級聯到相應的子類記錄、股票特權或股票的刪除上。我們也能夠定義一個參考完整性動作,這樣一個股票的刪除就級聯到關聯的股票特權記錄的刪除。
現在問題如下。如果我們刪除的一項資產是一隻股票,資產記錄的刪除級聯到引起股票記錄的刪除。隨後,股票記錄的刪除級聯引起所有股票特權記錄的刪除。但現在參考完整性使我們失敗了:一個股票特權記錄的刪除並不引起一項資產記錄的刪除。刪除級聯只能從超類走到子類。為了完全的行為,級聯應該雙向地走下去。
當前有用的參考完整性的工作是做更多的編程(也即做更多的工作和風險更多故障)。在我們的用例學習的實現裡,使用者隨時要刪除一項是股票的資產,我們不得不書寫額外的代碼來首先檢查關聯股票特權的存在性,然後刪除它們。 支援交叉表的記錄劃分。單獨繼承(泛化的最常見方式)的含義是一個超類的每個執行個體都是用多數只有一個子類來例示。現在的RDBMS不能容易地加強這個約束。例如,沒有什麼防止下面的情形。一隻股票可以用ID18加入到資產表,用ID18加入到股票表,並且也可以用ID18加入到股票特權表。再一次地,我們為了確信行為的完整,不得不作額外的編程,而不是寫一個簡單的聲明的約束
結論
本文陳述了用關係型資料庫實現UML模型的快速的概觀。我們希望本文向你示範的技術是足夠適宜的。一個訓練有素的開發人員能夠用關係型資料庫準備一套優秀的OO模型的實現。如果你要關於實現機制的更多的細節,參考3有另外的資訊,並且也覆蓋了我們沒有在這裡討論的一些進階模型建模結構。