2009-03-01 78 views
9

民間,關係數據庫中面向對象的結構

對於連續第n次,我再次遇到同樣的老問題。這是關於「如何以無痛的方式將OOP結構映射到數據庫表」。

這是一個場景:我的系統中有幾種類型的「演員」 - 工人,僱主,聯繫人。它們具有某些共同的功能;其他作品則大不相同。所有參與者處理的實體都是「通信」,「筆記」(管理員喜歡在客戶端留言)等等。每個演員類型處理的其他實體類型有很多種,而其他實體類型則沒有。

目前,我的數據庫架構包括表:

演員:

  • 工人
  • 僱主
  • 接觸

實體:

  • 通信
  • 筆記
實體和演員之間

關聯表:

  • 工人通信ASSN
  • 僱主通信ASSN
  • 工人筆記-assn
  • 等,你得到dril湖

這對我來說就像是一種「代碼味道」。每當顧客改變他們的角色時(即從「聯繫」提升到「僱主」),就需要運行一堆瘋狂的腳本。 Yuck ...另一方面,如果我在一個純粹的面向對象驅動的世界中運行,這將會容易得多 - 爲具有共同屬性的所有實體都有一個基類,並且可以完成它...

在數據庫世界中,這個選項在理論上似乎是可行的,但聽起來很混亂......也就是說如果我理解這個權利,我將擁有一個新的base_actor表,並且每個其他actor都有一個base_actor_id,然後這些關聯將位於base_actor和這些實體之間......但是,如何進行反向關聯查詢?即「向我展示只與工人類型的演員進行的所有交流」?

有什麼建議嗎?關於「將OOP結構映射到關係數據庫」的一般想法?

回答

6

這是我大約10年前提出的解決方案。使用這種設計的系統仍在運行,所以它的工作效果足以比我的大部分代碼生存得更久。 ;)今天我可以使用Scott提到的ORM包之一,但是直接使用SQL確實沒有什麼大問題。

  1. 將所有的繼承關係建模爲表之間的連接。系統中的每個表都將保存特定類的屬性。

  2. 使用合成對象id(oid)作爲所有對象的主鍵。序列生成器或自動增量列是生成oid值所必需的。

  3. 所有繼承的類必須使用與其父類相同的oid類型。使用級聯刪除將oid定義爲外鍵。父表獲取autoincrement oid列,子表單將獲得純oid列。

  4. 最終班級的查詢在相應的表格上進行。您可以將所有父類表連接到查詢中,也可以僅延遲加載所需的屬性。如果你的繼承層次很深並且你有很多類,一個ORM包可以真正簡化你的代碼。我的系統少於50個類,最大繼承深度爲3.

  5. 跨子類(即父類的查詢)的查詢可以延遲加載每個實例的子屬性,也可以重複每個用基類連接的子類的查詢。基於父類查詢延遲加載子屬性需要知道對象的類型。您可能在父類中有足夠的信息,但如果沒有,則需要添加類型信息。再次,這是ORM軟件包可以提供幫助的地方。

沒有成員屬性的虛擬類可以在表結構中被跳過,但是你將無法基於這些類進行查詢。

下面是「只顯示與工作人員的演員的所有通信」的樣子。

select * from comm c, worker w where c.actor=w.oid; 

如果您有溝通的子類,並且要立即加載所有子類屬性(也許你的系統不允許部分施工),最簡單的解決辦法是在所有可能的類渴望加盟。

select * from comm c, worker w, missive m where c.actor=w.oid and c.oid=m.oid; 
select * from comm c, worker w, shoutout s where c.actor=w.oid and c.oid=s.oid; 

最後一件事。確保你有一個好的數據庫和正確的索引。如果數據庫無法優化這些連接,性能會成爲一個嚴重問題。

3

您提出的建議似乎對我很有道理。您可以添加一個actortype列到您的actor-base表中,以區分不同類型的actor。每個特定的actor表的PK將是actorbase表的FK,以避免「毛病」的查詢並模仿繼承 - 就像'is-a'關係。

+0

作品一些,除了反向關聯查詢變得毛毛:從通訊C – 2009-03-01 22:48:51

+0

選擇姓名「告訴我,我已經在上個月發出信函給所有工人的名字」內部連接actor_base AB在c.actorid = ab.actorid其中ab.actortype ='工人' – Manu 2009-03-01 22:53:23

+0

和c.date之間的getdate()和dateadd(m,-1,getdtate()) – Manu 2009-03-01 22:54:45

2

我見過這種情況的最佳答案一直以: http://en.wikipedia.org/wiki/The_Third_Manifesto

不幸的是它不是適合在這裏計算器一個答案的空間。我會盡量在此縮寫,但我警告你,這樣的縮寫不會準確反映第三個宣言。請將所有關於此解決方案的批評重新引導至實際閱讀該死的東西,而不是假設您完全通過閱讀縮寫來理解它。好吧,在這裏。

定義了三個名爲worker,employer和contact的新列類型。將這些類型中的每一個的對象存儲在它們各自類型的列中。遵循數據模型其餘部分的標準化規範。我的感覺是,目前流行的數據庫技術實際上並不支持「正確」的方式去做這些事情(具體來說,許多數據庫系統不允許定義新類型)。所以不管你做什麼,你總會被迫陷入妥協的境地。但在閱讀第三個宣言後,至少你會知道你在妥協什麼。

ORM目前是目前絕大多數流行的解決方案,但我不相信這是正確的解決方案。

8

..這是關於「如何以無痛的方式將OOP結構映射到數據庫表」。

你不知道。

面向對象和關係代數是兩個根本不同的範例。沒有主觀解釋,你不能在它們之間轉換。這被稱爲阻抗不匹配,並被稱爲The Vietnam of Computer Science

0

對我來說,它只是看起來像你的數據模型缺少一個級別。我會設置它更像是這樣的:

人民表 - (關於實際的人只是信息)

角色表 - (該類型的角色的人能有即工人,僱主,聯繫 - 而具體到這個角色)

PeopleRoles信息表 - (people_id,ROLE_ID,也許開始/修改日期等)

實體表 - (定義不同類型的實體)

RoleEntities表 - (ROLE_ID,ENTITY_ID等)

然後從一個角色改變人到另一個(或允許他們有多個角色)是一個簡單的更新。

0

許多RDBMS提供了一個表繼承功能,該功能將父表與父類錶鏈接到子表的方式幾乎與類繼承相同。實施方式在供應商和供應商之間稍有不同,但實施類似概念可能會帶來一些麻煩。另外,大多數RDBMS都具有觸發器,存儲視圖和存儲過程的一些組合,這些組合可以將行爲與實現分開。在許多情況下,例如PostgreSQL的規則(視圖的泛化)提供了非常複雜的封裝,並且非常易於使用。

0

一些人已經注意到了對象 - 關係阻抗不匹配。最好的解決方案是簡單地放棄RDBMS,以支持最近重新流行的OODBMS。據我所知,在純PHP中沒有任何帶API的對象數據庫。一個快速搜索產生this result,但它並沒有在幾年內更新。另一方面,我聽說很多其他語言的對象數據庫,包括Hibernate,db4oZODB

1

this question所述,您可能很值得您熟悉對象角色建模。我看到的最大問題是沒有現成的關於關係數據的概念設計討論的方法。你能做的最好的是邏輯建模(通常是ERM)。對象角色建模爲該討論提供了基礎。我希望你能從類似的OOP設計討論中看到可識別的工件。