天天看點

細說資料庫設計之繼承關系映射

最近看了N多文章都講繼承的三種實作,最初是由淺入深Hibernate,接着是Teddy的關系總述,最後是孫亞民的《架構》。

這些文章所描述的實作方式都一模一樣:三種。第一種是整個體系一張表;第二種是每個具體類一張表;第三種是每個類一張表。繼承是OO技術,設計資料庫表則是資料庫技術,是以就稱之為關系映射技術,我覺得稱之為中繼資料設計比較更合理一點,讨論的基本上都是關于“抽象”的方法。我寫過一篇講中繼資料設計的文章,比較抽象一點,現在僅僅針對繼承,可以“細”一點說。其實我對Hierarchical研究得更透一些,正在籌劃一篇超長的文章來“細說”這種特殊的關系,繼承關系對我來說完全是業餘。

一、映射方法的評估

第一種方式,基本上沒有什麼意義,我定義為“濫用Nullable特性”。除了無端添加了一個描述類型的屬性外,這樣的備援簡直無法忍受,是以我根本不想讨論。我不明白那麼認真的老外們為什麼會認為這也算一種實作繼承的方式之一。

第二種方式比第一種方式要好一些,對對象層面來說就是在關系映射中放棄了繼承,否認實體的部分共性。這樣做最大的好處就是簡化了資料庫通路。對于簡單資料類型的屬性共享,通過表來分離雖然破壞了實體關系卻獲得了比較好的性能優勢,大部分情況下還是合算的。假如實體A和實體B共享大部分屬性,僅僅隻是A的某個屬性B沒有,或者B的某些屬性A沒有,用這種方式應該比較平衡。在一種情況下不能使用:如果A和B共享某個關系,這種方式就無法完成了。例如,A和B同時被C引用。例如,組織機構是一個抽象實體,下轄部門。組織分為三種,政府組織、企業組織和民間團體。按照每個具體類映射一張表的方法,組織機構的問題是解決了,但是部門沒法處理。根據我的經驗,在資料庫設計中繼承關系存在的唯一理由就是關系共享導緻的抽象。是以,第二種方式基本上不用考慮,原因是:根本沒有實作繼承,就是說這種方式實作的繼承根本就用不着繼承。

到此,映射繼承的方法隻剩下最後的一種了。事實上,第三種方法的确比較通用一些,幾乎可以處理各種可能出現的情況。我所知道的無法處理的情形之一是跨資料庫的抽象。上例的組織關系通過增加一張組織機構表,部門隻群組織機構發生關系即可。這種方式很容易了解。

二、展開的繼承關系

第一種展開:一拆再拆。

事實上,在領域中遇到的關系遠非組織和部門關系這麼簡單。假如,我們在上例中再增加一個實體:個人,并且再假定個人隸屬于部門。聽起來這是一個聚合關系,當你發現組織機構可以沒有部門的時候,你不得不把部門納入組織機構的繼承體系,同時還必須維持部門與組織機構間的聚合關系。其實事情還沒有完。部門是可以分級的,直接隸屬于政府組織、企業組織和民間團體的部門是頂級部門,它們的上級是獨立的組織機構;而隸屬于部門的部門與頂級部門之間還是有着很多的特異性,基于以上的讨論,需要将這兩種部門再次抽象,拆分成三個實體:部門、頂級部門和下級部門。

第二種展開:隐藏的體系關系(Hierarchization)。

一般來講,體系關系都是通過自引用來實作的,不關繼承什麼事兒,事實上更多的體系關系是通過繼承關系配合聚合來實作的。這種體系關系非常隐蔽,卻避免了通過自引用實作所帶來的“根陷阱”。換句話說,根節點和枝葉節點并不是同一個實體,而是同一個抽象實體的兩個具體實體。雖然,絕大部分設計師忽略了根節點的“某個屬性必須為空”這樣的備援,但并不等于這個備援并不存在。例如,實作一個檔案夾的體系。檔案夾是一個抽象類,根檔案夾和子檔案夾分别繼承自檔案夾,但子檔案夾有一個上級檔案夾這個屬性根檔案夾是沒有的,這個屬性并不指向子檔案夾自己,而是指向抽象檔案夾。其實這也是一種自引用,隻是并不是直接引用自己而是引用自己的抽象實體。

第三種展開:并行的體系(Inheritation)。

我們通常将根抽象類、中間抽象類到具體類的地圖稱之為繼承體系。由于“繼承關系存在的唯一理由就是關系共享導緻的抽象”,是以絕大部分繼承體系不是孤立和封閉的,往往有另外一棵或多棵繼承體系樹伴随。例如,上例中的組織機構可能都有一個決策機構,而不同的組織機構其決策機構是不同的,就必然形成另外一棵決策機構繼承體系樹。需要考慮的問題是:整體上是決策機構引用組織機構,具體實體間的關系映射卻非常複雜。基本上有兩種:根關系和葉關系。抽象的決策機構引用抽象的組織機構則為根關系,具體決策機構引用具體組織機構則為葉關系。兩種都可以,具體根據領域邏輯來定,但必須避免多重引用關系特别是隐藏的多重引用關系,導緻關系混亂。

三、繼承關系的限制

第一個限制是:在OO體系中,根類和中間類可以是抽象的,也可以是具體的,但是在映射到資料表之後卻隻能是抽象類。因為将同一張表中的具體類型執行個體與抽象類型執行個體很難分離。即使分離也是使用一些極不優雅的方法,例如增加一個标記字段或字段的某個标記域,違背了降冗優先的基本原則。是以,到葉一級很可能出現一些沒有任何屬性的實體,但是卻完全沒有了備援。

第二個限制是:不能采用複合鍵。複合鍵一直是我極度深惡痛絕的東西,我實在不明白這樣的東西存在的理由。複合鍵不同于組合鍵,組合鍵事實上還是單鍵,僅僅是必須以多個分離的字段來實作這個單鍵,映射到OO中其實就是兩個雙向的聚合(雙元組合)或者六個雙向聚合(三元組合),非常自然,也根本不會涉及到繼承。複合鍵卻不然。在派生類中通過複合鍵對應其超類,将極度困難!

第三個限制是:繼承關系的維護問題。普通關系的維護非常簡單,增加一個字段或者減少一個字段都沒有什麼問題。重構繼承關系的映射簡直就是一場惡夢,特别是如果還牽涉到曆史資料的遷移,那簡直就讓人抓狂了!是以,如果關系不夠穩定,要盡可以細地抽象,避免維護關系,以犧牲性能來換取可維護性。