2009-10-23 126 views
14

我在尋找適用於我的應用程序模型層的編程/設計模式,並且想知道哪種模型最適合您正在執行涉及跨多個表的連接的檢索的情況。用於數據庫表連接的最佳設計模式

例如,假設你有如下表/關係:客戶 - > 1..1賬戶 - > 0..n的特點

其中一個功能可能是一個支票本,或者一些高端產品比如免費旅行保險。

然後我想要getCustomersForFeature()檢索所有擁有免費旅行保險賬戶的客戶。

使用ActiveRecord或數據訪問對象似乎不合適,因爲它們通常集中在每個表的一個類上;同樣適用於Data Mapper。我意識到我可以將它分解成每個表的操作,例如getAccountsForFeature()和getCustomersForAccount(),但我想在一次打擊中進行檢索。

如果我們要「彎曲」每表一個類的模式並使用數據訪問對象模式,比如說,getCustomersForFeature()方法應該在CustomerDAO還是FeatureDAO上?但是這對我來說並不合適,因爲你會用其他表格的知識來污染你的DAO。

建議請。

+0

我已經爲此問題添加了社區Wiki答案,該答案基於這裏的答案和其他閱讀材料總結了我認爲的可用方法。這並不影響其他答案,但是這是我所期待的面向模式的方法,包含了總體思想。我猶豫選擇它作爲「接受的答案」(我可以這樣做我自己的答案?),直到它受到更多的審查。 – 2009-11-04 18:14:06

回答

1

在C#/ Linq to SQL中,它將如下所示。 (我假設實際上有一個要素類型的查找表,以便您有一個標準的要素類型列表,然後他們與一個賬戶的關係是分開的,所以FeatureTypeId將是您的FK值,也許從下拉列表中選擇名單什麼的。)

// or whatever data type your keys are in 
public IEnumerable<Customer> getCustomersForFeature(int featureTypeId) 
{ 
    return from feature in dbContext.Features 
      where feature.FeatureTypeId == featureTypeId 
      select getCustomer(feature.Account.Customer.Id); 
} 

在一定程度上你的DAO /包必須要知道你的對象之間的關係,所以實際上這是一個祖父母關係不應該是太嚇人了。

至於它在哪裏它屬於你的BAO結構,可能無論哪種方式都可能有爭論。我可能會把它放在客戶身上,因爲那最終是我試圖去的地方。

編輯:託比指出,關係是雙向的;再次Linq中,你可以走另外一條路,以及:

// or whatever data type your keys are in 
public IEnumerable<Customer> getCustomersForFeature(int featureTypeId) 
{ 
    return from customer in dbContext.Customers 
      from account in customer.Accounts 
      from feature in account.Features 
      where feature.FeatureTypeId == featureTypeId 
      select getCustomer(customer.Id); 
} 

任何其他的ORM應該有非常相似的行爲,即使語法和結構將發生變化。

2

Rails模式中的Active Record模式是通過允許Customer對象擁有have_many帳戶 - 這基本上轉化爲Account對象的集合,而Account對象的集合又具有一組Features。這些關係可以是雙向的,因此每個AR模型可以根據您的需求「瞭解」它們之間的關係。

我認爲對象具有其他表的知識是很好的,因爲這樣的關係對於OO和RDBMS/SQL都很重要。

+0

另外,您可以使用has_many:users,:through =>帳戶定義功能。 – 2009-10-23 12:22:48

+0

您能否詳細說明如何在封面下完成映射,例如檢索客戶是否會自動檢索關聯的帳戶和功能(即跨3個表的連接)還是這些惰性加載?如果我想檢索Customer */Account/Feature的一些*屬性(即子集),但不檢索整個對象圖,我該怎麼做? – 2009-10-26 14:45:06

+0

Rails中的Active Record非常複雜。默認行爲是集合將被延遲加載,這可能意味着額外的查詢。但是,通過在初始查找中使用「包含」機制可以加載數據(因此,當您加載客戶時,您告訴它加載賬戶,AR會爲您創建加入)。特定的字段也可以加載 - 您只需告訴它要加載哪些字段。 – 2009-10-27 00:15:41

0

通常,根據您正在使用的SQL數據庫的功能對數據進行建模。忽略ORM,映射器等

一旦你有一個好的數據模型,如果ActiveRecord或DAO不能以你想要的方式訪問它,那麼通過編寫SQL就可以繞過它們。

對象關係映射應該可以使數據庫編程變得更容易,但是因爲或者模型之間的差異,有時候簡單的方法是直接使用SQL。

+0

我很擔心忽略ORM等,因爲我想在儘可能少的地方(最好是一個)封裝如何映射到/來自數據庫的知識。噩夢場景正在改變數據庫結構,然後不得不查找所有受影響的查詢。你對ActiveRecord/DAO施加什麼限制(如果有的話),例如每桌一個班級? – 2009-10-26 14:51:20

+1

只需使用純SQL腳本,就可以在沒有任何ORM幫助的情況下進行數據庫遷移。這通常在具有訓練有素的DBA的大型數據庫安裝中完成。如果您要使用SQL數據庫,則必須中途兼顧DBA。有時候,採取他們的做法。對於應用程序特定數據庫中的簡單數據模型,ORM是可以的,但許多應用程序不擁有數據庫,它們只是幾個用戶之一。 – 2009-10-26 19:26:37

0

同意GalacticCowboy ORM允許將數據庫類映射細節與面向對象查詢分開。只是想爲Java ORM休眠提供了一個例子 - 有很多方法來定義association mappings和數量以下的面向對象的HQL查詢上面所有的人都工作正常

從客戶C左邊選擇C加入c.accounts左連接a.features f其中f.name = '酷'

注意'客戶'是一個類的名字在這裏和'c.accounts' 'a.features'「F。名稱'是類級別的屬性名稱,即沒有提到數據庫特定的細節。

8

Model-Driven Design中,您的邏輯實體實體位於您的應用程序中,並且這些實體可以映射到物理數據庫中的多個表。當然,您需要運行更復雜的SQL來獲取完整的實體,而Model類則是實現這些關係的地方。

我認爲ActiveRecord和他們的行爲可以用於對單個表進行簡單查詢,但試圖強制使用這些模式來處理複雜查詢太困難。幸運的是,我們已經有了一個簡潔的,特定於域的語言,您可以使用它來指定複雜的查詢:SQL。

因此,在您的Model類中,您將擁有執行邏輯應用程序級任務的方法,例如getCustomersForFeature()。在該方法的代碼中,您應該編寫一個特定的查詢,或者使用ActiveRecord方法,或者根據需要直接使用SQL。因此,您的數據庫的具體設計被封裝在Model類的一個地方。

這意味着我們需要打破模型和表格之間的耦合。 Model和ActiveRecord類之間的OO關係不是IS-A - 它是HAS-A(或has-many)。


重新評論:那麼我們的模型是什麼?如果您的應用程序主要需要作爲實體與客戶合作,並將功能視爲或多或少的客戶屬性,那麼您的模型就是客戶,它會隱藏功能存儲在數據庫的單獨表中的事實。客戶模型將內部使用ActiveRecord或普通SQL來收集所需的數據,以提供客戶的複雜對象及其關聯的多值屬性的完整視圖。

但是,如果您的應用程序也需要直接使用功能呢?例如,管理員的屏幕,您可以根據功能獲取報告或創建新功能。那麼通過Customer模型訪問功能將會很笨拙。所以你畢竟需要一個Features模型。只有它會有不同的方法來實現您需要使用功能的操作。

每個模型類都應該公開一個你需要做的事情的API 與那個模型。甚至不需要對稱。僅僅因爲您的客戶模型可以獲取具有給定功能的所有客戶,並不一定意味着您的功能模型需要獲取給定客戶的所有功能。遵循YAGNI規則。

但是,在創建了您的Customer模型和您的Features模型之後,這是否會導致知道表之間關係的邏輯的重複?是的,它可以。這是object-relational impedance mismatch範圍內的許多問題之一。

+0

有道理。所以在我給出的例子中,你會建議只創建一個CustomerModel嗎? – 2009-10-30 13:37:07

0

我認爲這是一個經典的問題(至少對我而言),您想在應用程序中表示您的數據模型。根據我自己的經驗,總會有一些像你所描述的那樣的交易。 我的解決方案與上面的一些答案非常相似。做一個簡單的類與你的表一對一,如果該表與另一個表有關係,然後作爲你的類中的屬性。

關於你說的'我想在一擊中做檢索。',我認爲你應該編寫自定義SQL查詢或者使用LINQ。

0

儘管對於實際的東西非常適合CRUD類型的表維護,但當涉及到複雜的查詢表時,我發現沒有什麼能夠擊敗實際的SQL。

ORM模型僅僅是一個抽象而已。

+0

是的,沒有。是的,因爲在某些情況下,無法使用ORM API來表示查詢,而且沒有,因爲如果您擁有強大的ORM(Hibernate/NHibernate)並且知道其API足夠好,這些情況很少。如果僅將它用於最簡單的CRUD操作,我認爲您會忽略ORM提供的大部分優點。 – Max 2009-10-29 08:05:08

0

要求oten爲您決定。無論是根據您的截止日期,您的工作文化或您的項目要求。您的截止日期可能表明,「代碼到工具」並削減功能。您的員工可能會要求「沒有新庫或失敗模式」。在我自己的環境中,你會聽到我講述這個故事:

「...它不會引入新的ORM庫,所以那些都沒有了。截止日期將表明我不應該開始自學我的工作文化告訴我快速完成它我的架構呻吟序列化整個表,我的性能要求表明我不應該緩存任何這些數據...「

SQL視圖可能會提供一種從對象中抽象連接的好方法,並且可以讓您更獨立於應用程序代碼來更改模式。更新視圖非常棘手,這取決於您的數據庫。如果DB便攜性很重要,那可能會給您的設計帶來很大的影響。

你可能會發現和諧的混合方法。如果您使用表格網關,則沒有理由將其嚴格聚合到表格的每個應用程序級別視圖中。爲什麼不使用表格網關或活動記錄來更新表範圍中的項目,以及處理視圖導向查詢的自定義類?

3

我花了一些時間來閱讀這個主題(包括在Bill的答案中引用的Domain Driven Design迷你書)。這本書中有一個總結概念,它最接近地代表了我想要實現的目標;客戶封裝並控制對賬戶和功能的訪問。如果我們堅持域驅動設計方法,我們可以使用存儲庫來控制客戶的檢索,並且在我看來,這是數據庫結構的知識將被封裝的地方。

我還看過我的企業應用程序體系結構模式副本,雖然看起來ActiveRecord模式旨在將類直接映射到數據庫表,但如果您選擇不遵循DDD的存儲庫方法那麼數據映射器將適合於複合映射。

謝謝大家的貢獻。我已經投票給出了這個結論的答案。由於這可能不是本次討論的最後一點,我已將此答案標記爲社區Wiki。