|
本文的目的是以最精煉的語言,理解什么是O/R Mapping,為什么要O/R Mapping,和如何進行O/R Mapping。
什么是O/R Mapping?
廣義上,ORM指的是面向?qū)ο蟮膶ο竽P秃完P(guān)系型數(shù)據(jù)庫的數(shù)據(jù)結(jié)構(gòu)之間的相互轉(zhuǎn)換。
狹義上,ORM可以被認為是,基于關(guān)系型數(shù)據(jù)庫的數(shù)據(jù)存儲,實現(xiàn)一個虛擬的面向?qū)ο蟮臄?shù)據(jù)訪問接口。理想情況下,基于這樣一個面向?qū)ο蟮慕涌冢志没粋€OO對象應(yīng)該不需要要了解任何關(guān)系型數(shù)據(jù)庫存儲數(shù)據(jù)的實現(xiàn)細節(jié)。
為什么需要O/R Mapping?
廣義上,因為我們需要面向?qū)ο髞砻枋鑫覀兊臉I(yè)務(wù),我們也需要關(guān)系型數(shù)據(jù)庫來存儲我們的數(shù)據(jù)。
有人可能會提到,我們未必要用面向?qū)ο髞砻枋鰳I(yè)務(wù),或者未必用關(guān)系型數(shù)據(jù)庫來存儲數(shù)據(jù)。沒錯,但是,至少,還是有相當大部分的程序,同時需要這兩者的合作。存在即合理。因為同時需要彼此,兩者都客觀存在,就值得討論。
既然從廣義上,存在即合理,無需討論為什么需要ORM,很多關(guān)于ORM的討論,其實都是針對上面提到的狹義的定義。但是即使到目前為止,真正能夠完美的實現(xiàn)這個狹義定義的ORM的工具,其實還并不存在(很多工具如Hibernate,已經(jīng)相當接近,但是,離完美還有相當距離)。
既然還不存在,那么,在討論需不需要之前,我們恐怕要先討論一下,是否可能,即從理論上,從數(shù)學上,面向?qū)ο蟮膶ο竽P秃完P(guān)系型數(shù)據(jù)庫的數(shù)據(jù)結(jié)構(gòu)之間,到底能不能做到完美的映射?要回答這個問題,我們要解決兩個難題。
O/R阻抗失衡
第一個難題,叫“O/R阻抗失衡(O/R Impedance Mismatch)”,指的是OO對象模型和關(guān)系型數(shù)據(jù)庫的數(shù)據(jù)結(jié)構(gòu)之間的,設(shè)計理念上的差異。OO的設(shè)計理念,是以最正確的語義來描述真實世界;而關(guān)系型數(shù)據(jù)庫的設(shè)計理念,則是從數(shù)學的角度,如何更有效的存儲和管理數(shù)據(jù)。由于兩者設(shè)計理念的差異,導(dǎo)致它們盡管從數(shù)據(jù)結(jié)構(gòu)上,可能很相近,但是關(guān)注點往往是不同的。
例如:
- 從OO的角度,凡是語義明確,每一個對象語義上的屬性,就應(yīng)該定義為一個屬性;從關(guān)系型數(shù)據(jù)庫的角度,有可能考慮到一些屬性從來不會被作為查詢條件,而把多個語義上的屬性,以一定的格式,存在一個數(shù)據(jù)表的字段中,也有可能因為一組語義上的屬性使用的頻度完全不同與另一組屬性,即使他們語義上屬于一個對象,也有可能將他們拆分成兩個數(shù)據(jù)表來存儲。
- 從OO的角度,對象只關(guān)心自己的固有屬性,不需要被唯一標識;但是,從關(guān)系型數(shù)據(jù)庫的角度,一般一個數(shù)據(jù)表中的每一行數(shù)據(jù)都需要要一個唯一標識,且很可能是語義上沒有意義的,如自增長的標識。
- 從OO角度的優(yōu)化,一般遵循SOLID這樣的原則,以更正確的語義來組織對象;從關(guān)系型數(shù)據(jù)庫角度的優(yōu)化,往往為了查詢性能,來修改字段的類型、長度,修改索引,甚至分表、分庫。
- ...
一個ORM工具想要通過簡單的配置,完美的解決“O/R阻抗失衡”問題,在我看來幾乎是不可能的。但是,一定程度上,通過靈活的配置支持絕大多數(shù)常見的映射策略,再加上提供可供用戶通過自定代碼來擴展的接口的話,應(yīng)該還是可以相當接近完美的。
文化阻抗失衡
第二個難題,叫“文化阻抗失衡(Cultural Impedance Mismatch)”,指的是關(guān)系型數(shù)據(jù)專家和面向?qū)ο髮<抑g的文化差異。關(guān)于這個難題的最經(jīng)典的爭論是“到底應(yīng)該以關(guān)系型數(shù)據(jù)庫的數(shù)據(jù)結(jié)構(gòu)來驅(qū)動,還是以O(shè)O的對象模型來驅(qū)動程序的開發(fā)?”。
關(guān)于這個爭論,OO專家的主要觀點是:
我的業(yè)務(wù)才是程序的核心,數(shù)據(jù)庫只是為我持久化數(shù)據(jù)的需求服務(wù)的,所以設(shè)計OO對象模型的時候,我不應(yīng)該考慮如何存儲到關(guān)系型數(shù)據(jù)庫的問題;
而數(shù)據(jù)庫專家的主要觀點是:
數(shù)據(jù)才是公司的核心財富,數(shù)據(jù)的穩(wěn)定性遠遠強于OO對象模型的穩(wěn)定性,由OO對像模型來驅(qū)動數(shù)據(jù)庫架構(gòu)的設(shè)計,根本保證不了性能,數(shù)據(jù)庫維護的成本根本不可接受。
其實,爭論的真正原因即在于“關(guān)系型數(shù)據(jù)專家和面向?qū)ο髮<抑g的文化差異”。大大牛Scott W. Ambler的文章Why Data Models Shouldn't Drive Object Models(And Vice Versa),很好的解答了這個問題。
歸根結(jié)底,既然OO和關(guān)系型數(shù)據(jù)庫都是不可缺少的部分,需要協(xié)同工作,希望做到完美的ORM,不僅僅需要好的ORM工具,更需要無論是OO專家還是數(shù)據(jù)庫專家互相了解對方的技術(shù)和設(shè)計理念。對一個OO專家,一個ORM工具,可以對其所支持的常見的映射,提供直接的支持,但是,應(yīng)用這些ORM工具的OO專家,必須了解關(guān)系型數(shù)據(jù)庫,知道如何在不影響OO對象的語義的前提下,如何設(shè)計一個更容易映射到關(guān)系型數(shù)據(jù)庫的對象模型,知道如何選擇ORM工具所支持的映射方式,以及如何用自定代碼擴展ORM所不能方便支持的映射方式;同樣的,對一個關(guān)系型數(shù)據(jù)庫的專家,也需要了解OO的語義,和可能發(fā)生的重構(gòu),從而,使得所設(shè)計的數(shù)據(jù)庫結(jié)構(gòu),更容易映射到OO對象,更容易響應(yīng)OO對象模型的重構(gòu)。如此才是真正的和諧,真正的完美。
小結(jié)
綜上所述,無論對一個OO專家還是對一個關(guān)系型數(shù)據(jù)庫專家,都需要了解OO,并了解關(guān)系型數(shù)據(jù)庫,了解ORM的基本原理。離開全面的知識,一個再完美的ORM工具也無法被正確使用,擁有這些知識,則能夠充分利用已有的ORM工具來加速自己的工作,并且合理的或擴展現(xiàn)有的ORM工具,或用自定代碼,實現(xiàn)ORM工具能力之外的ORM映射。這才是理想的ORM實踐。下一章節(jié),我們就來談?wù)勔幌拢琌RM的基本原理。
如何進行O/R Mapping?
簡單映射
1. Class <-> Table
一個Class一般可以映射為一個Table,一個Class的實例對應(yīng)Table的一行數(shù)據(jù)。但是,一個Table中的每行數(shù)據(jù),一般都需要有一個主鍵來唯一標識這行數(shù)據(jù),而一個Class的每個實例,則不一定需要一個唯一標識。
2. Property <-> Field
一個Class的Property一般可以直接映射為Table的一個Field。但是,他們的數(shù)據(jù)類型不一定直接匹配。如果他們代表的數(shù)據(jù)類型的語義上可轉(zhuǎn)換,則Field的類型,應(yīng)大于等于Property的數(shù)據(jù)類型。如果他們代表的類型語義上不可轉(zhuǎn)換,則需要在應(yīng)用程序?qū)用妫M行自定義的轉(zhuǎn)換。
繼承映射
1. 單表映射整個繼承體系
用一張數(shù)據(jù)庫表存儲整個繼承體系中的所有Class的數(shù)據(jù),數(shù)據(jù)表需要額外的標志字段來區(qū)分一行記錄應(yīng)該映射到繼承體系中的哪一個Class,適合繼承體系層次較少,總記錄數(shù)相對較少,子類對父類的屬性擴展也相對不那么頻繁的情形。
單表映射整個繼承體系的優(yōu)點是讀/寫繼承體系中的每個Class的數(shù)據(jù),都只需操作一張表,性能較好,并且,新增繼承類,或擴展Class屬性都只需要增減一張表的字段就可以了,易于維護;主要缺點是,因為繼承體系中所有的Class共享一張表,表中會有比較多的NULL字段值的數(shù)據(jù),浪費了一些存儲空間,同時,如果記錄數(shù)過多,表就會更龐大,也會影響表的讀寫性能。
2. 一個Class映射一個具體表
所謂一個Class映射一個具體表就是每個Class對應(yīng)一張數(shù)據(jù)表,并且,每個數(shù)據(jù)表冗余包含其父類的所有屬性字段,并且,子類和父類共享相同的主鍵值。一個Class一個具體表方案適合需要較高查詢性能,繼承體系層次不太復(fù)雜,并且基類包含較少的屬性而子類擴展較多屬性,并且能夠承受一定的數(shù)據(jù)庫冗余的情況。
一個Class映射一個具體表方案的優(yōu)點主要就是查詢性能好,讀操作只需操作一張表,和實體數(shù)據(jù)的對應(yīng)結(jié)構(gòu)清晰,數(shù)據(jù)庫表遷移和維護會比較方便;主要的缺點是數(shù)據(jù)冗余較大,因為每次插入一條子類數(shù)據(jù)時,同時要插入一份子類包含的父類字段的數(shù)據(jù)到所有父類層次表中。
3. 一個Class映射一個擴展表
所謂一個Class映射一個擴展表是指繼承體系中的每個Class對應(yīng)一張數(shù)據(jù)表,但是,每個子類不冗余包含父類的所有屬性,而只是包含擴展的屬性和共享的主鍵值。一個Class映射一個擴展表方案適合繼承體系非常復(fù)雜,結(jié)構(gòu)易變,并希望最大程度減少數(shù)據(jù)冗余的情形。
一個Class映射一個擴展表方案的優(yōu)點是結(jié)構(gòu)靈活,新增子類或插入中間的繼承類都很方便,冗余數(shù)據(jù)最少;但是缺點是,無論讀還是寫操作都會涉及到子類和所有的父類。讀操作時,必須自然鏈接查詢所有的父類對應(yīng)的數(shù)據(jù)表,而插入或更新數(shù)據(jù)時,也需要寫所有的父類表。
4. 通用的表結(jié)構(gòu)映射所有的Class
這種方案其實不僅支持用一張表存儲一個繼承體系,它甚至可以支持,用一張表存儲任意數(shù)量的不同Class。它的原理是元數(shù)據(jù)驅(qū)動。這張表的每一行,包含一個類型的標識字段,一個表示Class的屬性名稱的字段和一個表示Class的屬性值的字段。在運行時,通過唯一標識取出描述一個Class實例的所有Property的值,再根據(jù)Property的名稱來映射。
關(guān)于各種繼承映射的實例分析和詳細的優(yōu)劣比較,請參見:Scott W. Ambler的O/R Mapping In Detail。
關(guān)聯(lián)映射
1. 一對一關(guān)聯(lián)、一對多關(guān)聯(lián)(包含一對一和一對多的自關(guān)聯(lián))
所謂一對一關(guān)聯(lián),實際上還可以分為三種情形,即0..1 - 1,1 – 1,1 – 0..1三種情形;而一對多關(guān)聯(lián)則分為* - 1和1 - *。
以下三種方案中第1)種為最常用的映射方案,后面幾種是在某些特殊情形下可參考的方案:
1) 最常用的方案為為需要其他對象引用的類對應(yīng)的表增加一個到被引用對象對應(yīng)表的外鍵即可,只不過,與表對應(yīng)的實體類代碼中,對于一對多情形下的“多”這一端,需定義成集合類型;
2) 在Hibernate稱為“組件(Component)映射”,舉例來說,假如Person類包含一個Address成員類型的屬性,而Address由City,Street,ZipCode三個成員屬性組成,假如Address除了與Person關(guān)聯(lián)不被其他對象使用,則我們可以考慮只用一張數(shù)據(jù)表Person來持久化Person和Address這兩張表,Person數(shù)據(jù)表包含Person類中除Address的屬性和Address類中的所有屬性的集合,當然,這時需要在元數(shù)據(jù)中特別指明映射關(guān)系;
3) 還有一種針對上面的方案中的Person,Address兩個類的持久化方案則是將Address類型的所有屬性先序列化,再存入Person表的字段Address中,這樣也可以只用一張表來持久化兩個類,當然,本方案中這種被序列化對象成員數(shù)據(jù)量應(yīng)盡量小;
4) 還有一種方案是共享同一主鍵值的一對一關(guān)聯(lián)。即將原本可以同屬于一個表中相對使用不太頻繁的字段提出來放在另一張表中,這樣,這兩張表的記錄就可以通過一個相同的主鍵進行關(guān)聯(lián)。
2. 多對多關(guān)聯(lián)(包含多對多的自關(guān)聯(lián))
所謂多對多關(guān)聯(lián)自然就是* - *這種情形了。一般都需要一張包含關(guān)聯(lián)雙方主鍵的關(guān)聯(lián)表,在取數(shù)據(jù)時,需要鏈接該關(guān)聯(lián)表和數(shù)據(jù)表。
英文資料
- http://en.wikipedia.org/wiki/Object-relational_mapping
- http://www.agiledata.org/essays/impedanceMismatch.html
- http://www.agiledata.org/essays/culturalImpedanceMismatch.html
- http://www.agiledata.org/essays/drivingForces.html
- http://www.agiledata.org/essays/mappingObjects.html
it知識庫:理解O/R Mapping,轉(zhuǎn)載需保留來源!
鄭重聲明:本文版權(quán)歸原作者所有,轉(zhuǎn)載文章僅為傳播更多信息之目的,如作者信息標記有誤,請第一時間聯(lián)系我們修改或刪除,多謝。