對象方式的基礎是用具有數據和行為的對象來(lái)構建應用;關(guān)系方式的基礎是將數據存儲在表的行中。在將對象用關(guān)系數據庫存儲時(shí),自然存在對象/關(guān)系阻抗不匹配。它們的本質(zhì)區別使得這兩種方式結合的并不完美。
要將對象成功映射到關(guān)系數據庫,就要了解這兩種方式及其區別,從中選擇折衷的方案。在此引用《過(guò)程模式(上)》(Scott.W.Ambler著(zhù))原文:
要減少對象/關(guān)系阻抗不匹配的影響,您需要:
.使用對象標識符
.掌握映射的基本知識
.避免存儲過(guò)程
.了解將對象映射到關(guān)系型數據庫的現實(shí)情況
1
. 對象標識符
對象標識符OID唯一標識對象,不會(huì )發(fā)生重復。在關(guān)系數據庫的表中,需要選擇一個(gè)主屬性,以標識不同的元組。每個(gè)元組的各個(gè)屬性均涉及到業(yè)務(wù)含義,而具有任何業(yè)務(wù)含義的元組屬性再未來(lái)都可能會(huì )改變。引用含業(yè)務(wù)關(guān)系的屬性作為主鍵,一旦業(yè)務(wù)發(fā)生變化,維護工程將是非常龐大的。而OID是不具有任何業(yè)務(wù)含義的,這也是其最大的優(yōu)點(diǎn)。如果將OID作為主鍵,一旦業(yè)務(wù)變化,修改的僅僅是數據庫的表,并不會(huì )對應用造成影響。這種方式的不足之處就是它不能完全解決我們在對象間導航的問(wèn)題。
使用OID作為主鍵的另一好處就是它使我們容易地自動(dòng)維護對象間的關(guān)系,可以寫(xiě)一組通用的代碼維護應用。
2
. 映射的基本原理
將對象映射到表的列中的時(shí)候,一個(gè)屬性對應一列,但應去掉不是持久的屬性。一些對象的有些屬性本身可能是對象,所以有時(shí)候需要將一個(gè)OO屬性映射到數據庫的多列。記?。喝魏谓o定的屬性將被映射到零個(gè)/多個(gè)列。
存在繼承關(guān)系的對象如何映射到關(guān)系數據庫?也可以理解為如何組織數據庫中已繼承的屬性。有如下方案可選擇:
1)
. 對整個(gè)類(lèi)層次結構使用一個(gè)表:層次結構中所有類(lèi)的屬性都存儲在一張表中。這種方式簡(jiǎn)單,容易生成任意報表。但是當類(lèi)層次結構中的某一處增加了新屬性,就需要增加新列,這也就增加了類(lèi)層次結構中的耦合。如果添加的屬性錯誤,將會(huì )影響到該層次結構中所有的類(lèi),而不僅僅是獲得新屬性的那個(gè)子類(lèi)。同時(shí)它也浪費了數據庫的空間,不是每一列都適合表中的每個(gè)對象。
2)
.每個(gè)具體的類(lèi)使用一個(gè)表:每個(gè)表都包含所代表的類(lèi)的屬性及其繼承屬性。這種方式生成報表也比較容易。但當修改一個(gè)類(lèi)時(shí),就需要修改其映射表及其子類(lèi)的表。還有就是,當對象更改其角色,它的數據就需要復制到對應的表中,同時(shí)要為其指定新的OID,在數據較多的時(shí)候,工作量也不容忽視。最后,在對象支持多角色的時(shí)候,難以保持數據完整性。
3)
.每個(gè)類(lèi)使用一個(gè)表:這樣的類(lèi)的屬性是OID及特定于該類(lèi)的那些屬性。這種方式最符合面向對象的概念,支持多態(tài)(只需在對象可能擁有角色的相應表中保留記錄)。修改超類(lèi)和添加子類(lèi)也比較容易,只需要修改/添加一個(gè)表(外加維護關(guān)系的表,如果有)。這種方式缺點(diǎn)就是讀寫(xiě)數據的時(shí)間加長(cháng),可以通過(guò)將類(lèi)層次結構的每個(gè)表放在不同的物理磁盤(pán)驅動(dòng)器上進(jìn)行巧妙的組織數據庫。在不添加試圖模擬所需的表的時(shí)候,生成任意報表就比較困難了。
從數據庫角度,關(guān)聯(lián)和聚合的唯一區別就是對象間綁定的緊密程度。使用聚合,讀入的所有數據實(shí)際上是需要的部分,而使用關(guān)聯(lián),需要執行的操作并不是那么明顯。關(guān)系數據庫中的關(guān)系是通過(guò)外鍵來(lái)維護,在一對一和一對多的關(guān)系中,外鍵能夠維持。在多對多的關(guān)系中,我們應引入關(guān)聯(lián)表,它的唯一用途就是維護關(guān)系數據庫的兩個(gè)表或多個(gè)表間的關(guān)系。
3
. 避免存儲過(guò)程
存儲過(guò)程是在20世紀80年代為了支持結構化開(kāi)發(fā)者構建c/s應用需要而引入的。對結構化開(kāi)發(fā)是一個(gè)好辦法,但并不適用面向對象的開(kāi)發(fā)。
將對象映射到關(guān)系數據庫時(shí),不要使用存儲過(guò)程。原因如下:
1) 使用該方式,數據庫很快成為整個(gè)應用的瓶頸。存儲過(guò)程是在關(guān)系數據庫服務(wù)器上運行的功能,如果經(jīng)常調用,即使一個(gè)簡(jiǎn)單的存儲過(guò)程也會(huì )使服務(wù)器不堪重負。
2) 存儲過(guò)程是用專(zhuān)門(mén)的語(yǔ)言編寫(xiě)的,這也可能成為中斷運行的因素。隨著(zhù)時(shí)間推移,一旦更換數據庫供應商,變化的影響就會(huì )暴露出來(lái)。
3) 這種方式極大增加了數據庫中的耦合。存儲過(guò)程直接訪(fǎng)問(wèn)各個(gè)表,這些表與存儲過(guò)程耦合,所增加的耦合降低了DBA的靈活性(需要重新組織數據庫,重新編寫(xiě)存儲過(guò)程)。這同時(shí)也增加了開(kāi)發(fā)者的維護負擔。
當構建打算拋棄的原型(假設尚未構建堅固的持久層)或映射到一個(gè)舊的數據庫而其設計完全不適合對象且不能根據特定的需要改造該數據庫時(shí),選擇存儲過(guò)程是有意義的。對后一種情況,無(wú)需使用存儲過(guò)程編寫(xiě)這些代碼,可以使用自選的語(yǔ)言編寫(xiě)并在數據庫外運行它(避免網(wǎng)絡(luò )流量,故可能需要放在服務(wù)器上)
4
. 對象映射到關(guān)系數據庫的現實(shí)
對象和關(guān)系數據庫是常見(jiàn)的情況。
硬編碼sql極不可?。涸黾恿藨门c數據庫設計耦合,可維護性較差。最好的辦法是在持久層動(dòng)態(tài)生成sql,雖然動(dòng)態(tài)sql速度較慢,但維護性得到增強。
數據模型無(wú)法驅動(dòng)類(lèi)圖:需要映射到舊數據庫時(shí),不應該降低對象設計的質(zhì)量,不要用數據模型做類(lèi)圖設計的基礎。
表連接很慢:在合理的地方跨越而非連接是克服對象/關(guān)系不匹配的一個(gè)選擇。
避免使用組合鍵。它作為外鍵會(huì )增加數據庫的開(kāi)銷(xiāo),增加了數據庫設計的復雜度,而且在處理多字段時(shí)會(huì )導致附加處理需求。
注:本文摘自Scotte.W.Ambler的《過(guò)程模式(上)》,并稍做修改整理