深入理解O R Mapping

2021-08-31 04:53:23 字數 4223 閱讀 2394

廣義上,orm指的是物件導向的物件模型和關係型資料庫的資料結構之間的相互轉換。

狹義上,orm可以被認為是,基於關係型資料庫的資料儲存,實現乙個虛擬的物件導向的資料訪問介面。理想情況下,基於這樣乙個物件導向的介面,持久化乙個oo物件應該不需要要了解任何關係型資料庫儲存資料的實現細節。

廣義上,因為我們需要物件導向來描述我們的業務,我們也需要關係型資料庫來儲存我們的資料。

有人可能會提到,我們未必要用物件導向來描述業務,或者未必用關係型資料庫來儲存資料。沒錯,但是,至少,還是有相當大部分的程式,同時需要這兩者的合作。存在即合理。因為同時需要彼此,兩者都客觀存在,就值得討論。

既然從廣義上,存在即合理,無需討論為什麼需要orm,很多關於orm的討論,其實都是針對上面提到的狹義的定義。但是即使到目前為止,真正能夠完美的實現這個狹義定義的orm的工具,其實還並不存在(很多任務具如hibernate,已經相當接近,但是,離完美還有相當距離)。

既然還不存在,那麼,在討論需不需要之前,我們恐怕要先討論一下,是否可能,即從理論上,從數學上,物件導向的物件模型和關係型資料庫的資料結構之間,到底能不能做到完美的對映?要回答這個問題,我們要解決兩個難題。

o/r阻抗失衡

第乙個難題,叫「o/r阻抗失衡(o/r impedance mismatch)」,指的是oo物件模型和關係型資料庫的資料結構之間的,設計理念上的差異。oo的設計理念,是以最正確的語義來描述真實世界;而關係型資料庫的設計理念,則是從數學的角度,如何更有效的儲存和管理資料。由於兩者設計理念的差異,導致它們儘管從資料結構上,可能很相近,但是關注點往往是不同的。

例如:乙個orm工具想要通過簡單的配置,完美的解決「o/r阻抗失衡」問題,在我看來幾乎是不可能的。但是,一定程度上,通過靈活的配置支援絕大多數常見的對映策略,再加上提供可供使用者通過自定**來擴充套件的介面的話,應該還是可以相當接近完美的。

文化阻抗失衡

第二個難題,叫「文化阻抗失衡(cultural impedance mismatch)」,指的是關係型資料專家和物件導向專家之間的文化差異。關於這個難題的最經典的爭論是「到底應該以關係型資料庫的資料結構來驅動,還是以oo的物件模型來驅動程式的開發?」。

關於這個爭論,oo專家的主要觀點是:

我的業務才是程式的核心,資料庫只是為我持久化資料的需求服務的,所以設計oo物件模型的時候,我不應該考慮如何儲存到關係型資料庫的問題;

而資料庫專家的主要觀點是:

資料才是公司的核心財富,資料的穩定性遠遠強於oo物件模型的穩定性,由oo對像模型來驅動資料庫架構的設計,根本保證不了效能,資料庫維護的成本根本不可接受。

其實,爭論的真正原因即在於「關係型資料專家和物件導向專家之間的文化差異」。大大牛scott w. ambler的文章why data models shouldn't drive object models(and vice versa)

,很好的解答了這個問題。

歸根結底,既然oo和關係型資料庫都是不可缺少的部分,需要協同工作,希望做到完美的orm,不僅僅需要好的orm工具,更需要無論是oo專家還是資料庫專家互相了解對方的技術和設計理念。對乙個oo專家,乙個orm工具,可以對其所支援的常見的對映,提供直接的支援,但是,應用這些orm工具的oo專家,必須了解關係型資料庫,知道如何在不影響oo物件的語義的前提下,如何設計乙個更容易對映到關係型資料庫的物件模型,知道如何選擇orm工具所支援的對映方式,以及如何用自定**擴充套件orm所不能方便支援的對映方式;同樣的,對乙個關係型資料庫的專家,也需要了解oo的語義,和可能發生的重構,從而,使得所設計的資料庫結構,更容易對映到oo物件,更容易響應oo物件模型的重構。如此才是真正的和諧,真正的完美。

小結綜上所述,無論對乙個oo專家還是對乙個關係型資料庫專家,都需要了解oo,並了解關係型資料庫,了解orm的基本原理。離開全面的知識,乙個再完美的orm工具也無法被正確使用,擁有這些知識,則能夠充分利用已有的orm工具來加速自己的工作,並且合理的或擴充套件現有的orm工具,或用自定**,實現orm工具能力之外的orm對映。這才是理想的orm實踐。下一章節,我們就來談談一下,orm的基本原理。

簡單對映

1. class <-> table

乙個class一般可以對映為乙個table,乙個class的例項對應table的一行資料。但是,乙個table中的每行資料,一般都需要有乙個主鍵來唯一標識這行資料,而乙個class的每個例項,則不一定需要乙個唯一標識。

2. property <-> field

乙個class的property一般可以直接對映為table的乙個field。但是,他們的資料型別不一定直接匹配。如果他們代表的資料型別的語義上可轉換,則field的型別,應大於等於property的資料型別。如果他們代表的型別語義上不可轉換,則需要在應用程式層面,進行自定義的轉換。

繼承對映

1. 單錶對映整個繼承體系

用一張資料庫表儲存整個繼承體系中的所有class的資料,資料表需要額外的標誌欄位來區分一行記錄應該對映到繼承體系中的哪乙個class,適合繼承體系層次較少,總記錄數相對較少,子類對父類的屬性擴充套件也相對不那麼頻繁的情形。

單錶對映整個繼承體系的優點是讀/寫繼承體系中的每個class的資料,都只需操作一張表,效能較好,並且,新增繼承類,或擴充套件class屬性都只需要增減一張表的字段就可以了,易於維護;主要缺點是,因為繼承體系中所有的class共享一張表,表中會有比較多的null字段值的資料,浪費了一些儲存空間,同時,如果記錄數過多,表就會更龐大,也會影響表的讀寫效能。

2. 乙個class對映乙個具體表

所謂乙個class對映乙個具體表就是每個class對應一張資料表,並且,每個資料表冗餘包含其父類的所有屬性字段,並且,子類和父類共享相同的主鍵值。乙個class乙個具體表方案適合需要較高查詢效能,繼承體系層次不太複雜,並且基類包含較少的屬性而子類擴充套件較多屬性,並且能夠承受一定的資料庫冗餘的情況。

乙個class對映乙個具體表方案的優點主要就是查詢效能好,讀操作只需操作一張表,和實體資料的對應結構清晰,資料庫表遷移和維護會比較方便;主要的缺點是資料冗餘較大,因為每次插入一條子類資料時,同時要插入乙份子類包含的父類字段的資料到所有父類層次表中。

3. 乙個class對映乙個擴充套件表

所謂乙個class對映乙個擴充套件表是指繼承體系中的每個class對應一張資料表,但是,每個子類不冗餘包含父類的所有屬性,而只是包含擴充套件的屬性和共享的主鍵值。乙個class對映乙個擴充套件表方案適合繼承體系非常複雜,結構易變,並希望最大程度減少資料冗餘的情形。

乙個class對映乙個擴充套件表方案的優點是結構靈活,新增子類或插入中間的繼承類都很方便,冗餘資料最少;但是缺點是,無論讀還是寫操作都會涉及到子類和所有的父類。讀操作時,必須自然鏈結查詢所有的父類對應的資料表,而插入或更新資料時,也需要寫所有的父類表。

4. 通用的表結構對映所有的class

這種方案其實不僅支援用一張表儲存乙個繼承體系,它甚至可以支援,用一張表儲存任意數量的不同class。它的原理是元資料驅動。這張表的每一行,包含乙個型別的標識字段,乙個表示class的屬性名稱的字段和乙個表示class的屬性值的字段。在執行時,通過唯一標識取出描述乙個class例項的所有property的值,再根據property的名稱來對映。

關聯對映

1. 一對一關聯、一對多關聯(包含一對一和一對多的自關聯)

所謂一對一關聯,實際上還可以分為三種情形,即0..1 - 1,1 – 1,1 – 0..1三種情形;而一對多關聯則分為* - 1和1 - *。

1) 最常用的方案為為需要其他物件引用的類對應的表增加乙個到被引用物件對應表的外來鍵即可,只不過,與表對應的實體類**中,對於一對多情形下的「多」這一端,需定義成集合型別;

2) 在hibernate稱為「元件(component)對映」,舉例來說,假如person類包含乙個address成員型別的屬性,而address由city,street,zipcode三個成員屬性組成,假如address除了與person關聯不被其他物件使用,則我們可以考慮只用一張資料表person來持久化person和address這兩張表,person資料表包含person類中除address的屬性和address類中的所有屬性的集合,當然,這時需要在元資料中特別指明對映關係;

3) 還有一種針對上面的方案中的person,address兩個類的持久化方案則是將address型別的所有屬性先序列化,再存入person表的字段address中,這樣也可以只用一張表來持久化兩個類,當然,本方案中這種被序列化物件成員資料量應盡量小;

4) 還有一種方案是共享同一主鍵值的一對一關聯。即將原本可以同屬於乙個表中相對使用不太頻繁的字段提出來放在另一張表中,這樣,這兩張表的記錄就可以通過乙個相同的主鍵進行關聯。

2. 多對多關聯(包含多對多的自關聯)

所謂多對多關聯自然就是* - *這種情形了。一般都需要一張包含關聯雙方主鍵的關聯表,在取資料時,需要鏈結該關聯表和資料表

深入理解C語言 深入理解指標

關於指標,其是c語言的重點,c語言學的好壞,其實就是指標學的好壞。其實指標並不複雜,學習指標,要正確的理解指標。指標也是一種變數,占有記憶體空間,用來儲存記憶體位址 指標就是告訴編譯器,開闢4個位元組的儲存空間 32位系統 無論是幾級指標都是一樣的 p操作記憶體 在指標宣告時,號表示所宣告的變數為指...

mysql 索引深入理解 深入理解MySql的索引

為什麼索引能提高查詢速度 先從 mysql的基本儲存結構說起 mysql的基本儲存結構是頁 記錄都存在頁裡邊 各個資料頁可以組成乙個雙向鍊錶每個資料頁中的記錄又可以組成乙個單向鍊錶 每個資料頁都會為儲存在它裡邊兒的記錄生成乙個頁目錄,在通過主鍵查詢某條記錄的時候可以在頁目錄中使用二分法快速定位到對應...

深入理解C語言 深入理解指標

關於指標,其是c語言的重點,c語言學的好壞,其實就是指標學的好壞。其實指標並不複雜,學習指標,要正確的理解指標。指標也是一種變數,占有記憶體空間,用來儲存記憶體位址 指標就是告訴編譯器,開闢4個位元組的儲存空間 32位系統 無論是幾級指標都是一樣的 p操作記憶體 在指標宣告時,號表示所宣告的變數為指...