對象和數據庫的天然阻抗

原文地址:http://www.jdon.com/mda/oo-reltaion2.html

 在“面向對象建模與數據庫建模兩種分析設計方法的比較”一文中我們比較了在對需求分析時兩種方法的不同,所謂數據庫建模分析,就是項目一開始就根據需求建立數據庫模型,如數據表結構和字段等,這種錯誤現象大量普遍存在我們國內項目實踐中,從每年大量招聘啓示中就可見一斑:招聘數據庫建模人員,招聘Java面向對象程序員。這些說明軟件業一邊在大量使用Java/.NET/Ruby on Rails這樣OO語言同時,還在同時使用與OO體系抵觸的圍繞關係數據庫的分析設計方法。

  爲進一步說明OO和關係數據庫是屬於兩個不同世界觀,存在天然矛盾,就象有神論和無神論,就象水與火那樣屬於截然相反的兩種編程知識。正好最近TheServerSide刊登一篇大談對象數據庫ODBMS的文章:When to use an Embedded ODBMS(以下簡稱ODBMS一文)
http://www.theserverside.com/tt/articles/article.tss?l=EmbeddedODBMS

  這篇文章不但談到了OO和關係數據庫天然阻抗;談到了ODBMS和RDBMS區別,也談到了ODBMS和O/R Mapping如Hibernate的區別,甚至談到了ODBMS在非C/S如嵌入式方面的優點。這篇文章我看最重要價值就是它用大量篇幅闡述了對象和關係數據庫存在的天然矛盾,我這裏就將這部分篇幅大意轉載這裏,可以說是經過我咀嚼後反饋的結果,版權思想還是屬於這篇文章,我這裏只是用中國人更易於接受方式把這個天然矛盾轉述出來:

對象和關係數據庫累贅轉換

  在一個面向對象系統中,數據存在於對象中,在關係數據庫中,數據存在於數據表的記錄中。
在關係數據庫世界中,我們必須掌握表結構、表記錄等概念,這裏面沒有任何對象概念,當我們在OO語言中使用關係數據庫時,因爲在OO世界中是大量對象,所以必須將數據在這兩個不同世界之間轉換傳輸。

   這就導致了大量垃圾臨時對象,增加了應用系統的複雜性。這也是我們很多人使用了Java等這樣的OO系統後,反而覺得開發效率並沒有OO宣傳那麼高的原因, 說白了,OO思想沒有完全佔領應用系統,因爲有關係數據庫盤踞着數據庫這個最後堡壘負隅頑抗。

  下面看看我們程序如何爲了讓OO語言和關係數據庫捆綁在一起工作,如何彌補兩個世界的裂縫,如何在他們之間和稀泥。 爲了將對象保存到關係數據庫中,我們必須將對象中的數據解散開來,再將它們組裝到SQL中,然後執行SQL。
一個類如下:


public class Course {
  public string name;
  public int courseID;
  int deptID;
  public int creditHours;
}

將上述對象保存到關係數據庫的SQL語句如下:


PreparedStatement insertStatement = connection.prepareStatement(
"INSERT INTO Courses (name, courseID, deptID, creditHourse) " +
"VALUES (?, ?, ?, ?)");
insertStatement.setString(1, course.name);
insertStatement.setInt(2, course.courseID);
insertStatement.setInt(3, course.deprtID);
insertStatement.setInt(4, course.creditHours);
insertStatement.executeUpdate();

上述在兩個世界之間做的翻譯工作與複雜性取決於對象Course這樣的複雜性。如果Course字段有十幾個,那麼這個保存SQL將更復雜。

   更重要的是,萬一有一個字段發生變化,更改量就很大。我們不但要修改對象,而且還要修改數據表結構,有過數據庫編程經驗的人知道,如果表中有數據,表結構變動帶來的問題可能就象噩夢一樣,所以,我們程序員經常以這個需要更改表結構拒絕一些軟件的維護和拓展,這其實更是錯上加錯;在ODBMS一文中提出了對象數據庫解決方案:只要更改類結構,其他都有ODBMS搞定;使用ORM如Hibernate也只多一個步驟:更改映射配置。

繼承關係的尷尬實現

  對象和關係數據庫的不匹配還表現在關係數據庫難以實現對象世界中的繼承關係,如下圖:


  這是一個典型的對象世界表達客觀世界的類圖,學生和教授都是人,都具備人一些共性,當然他們之間也有區別,所以我們用上述繼承關係來表達這樣一個情況。

  那麼如果這樣繼承關係的類圖如何保存到關係數據庫中呢?有兩種方式:one-class-per-table(一個類對應一個表)和one-object-per-row(一個對象對應一個錶行)。

  在one-class-per-table方式中,Student對象放在Student表中,Professor放在Professor表中。但是Student和Professor實際具有共性Person,這實際上將Person這個共性對象中數據分割成兩個表保存,從語義上所:也就是將一個對象分割成兩個部分了;而且當你要獲取這個對象時,需要兩次Select。同樣道理增刪改查都要兩次。

  如果按照one-object-per-row,將這Student和Professor放到一個表中,就會產生空白字段。Student對象中數據保存到Student對象對應的字段中,那麼Professor對象對應的字段就是空的,反之也然。

  這些都反映了對象和關係數據庫天然不匹配,兩者根本就無法搭配在一起工作,只有一方作出妥協,大部分情況是對象作出妥協,這樣,一個OO語言Java/.NET系統就做成了一個數據庫中心繫統,根本無法享受OO語言帶來快捷方便,可維護性強,由於Java出現比較早,更多程序員將項目失敗歸結於Java語言本身,轉而使用數據庫思維去做.NET,去做Ruby On Rails,還是會碰到上面這些問題,沒有碰到,只能說明他們系統中就沒有對象概念,解決方式很簡單很可笑:矛盾雙方,消滅一方不久解決矛盾了。

類的複雜關係實現

  下面我們再看看ODBMS一文中列舉的類複雜關係如何用關係數據庫實現保存:


public class Department {
    private Professor[] professors;
... }

  這個Department類很明確告訴我們這樣一個意思,在一個Department中,存在很多Professor,大白話就是:一個部門裏有很多教授,這個類就自然準確地表達這個意思,這也體現了使用OO表達需求的自然性和方便性。

  那麼我們下面看看,如果我們使用關係數據庫來保存Department這個對象,很顯然,這裏需要使用關係數據庫的表外鍵,通過表外鍵來表達Department表和Professor之間1:N關係,關鍵問題是:這個外鍵一般是設計在Professor表中,這就造成語義上的誤解。

  對象Department表達的需求含義是:
  1. 我是一個部門對象,在我裏面包含很多教授對象。

  當這個對象在關係數據庫,由於外鍵在Professor表中,那麼意思就是:
  2. 我是一個教授對象,這裏有一個我所屬的部門對象。

  前後對比發現,其實完全是兩者不同表達方式,這已經屬於拷貝走樣了,當我們從關係數據庫中獲得Department 對象時,得按照關係數據庫規則繞一個彎來獲得完整的Department 對象。也就是說:每次獲得一個對象Department,我們得將這個需求意思翻譯成關係數據庫的表達方式,這種內耗式的翻譯不但容易出錯,也帶來了程序員開發上的負擔。萬一不留神,翻譯出錯,問題就嚴重了。

  當然,如果我們使用ODBMS,就不必做這些費力不討好的翻譯轉換,也不再繞着彎子編程,只要直接告訴ODBMS或ORM框架如Hibernate,就能夠獲得一個真正對象意義上的Department。

  以上是TSS的ODBMS一文大量篇幅闡述了對象和關係數據庫矛盾的方面,所以,我們才認爲:如果你使用對象語言,如Java/.NET/Ruby On Rails等,那麼就必須堅持OO思想和方法,從程序中杜絕關係數據庫對軟件的影響,將關係數據庫只看成是活動對象的“冬眠”(英文Hibernate)地方,這也就是ORM框架Hibernate的本義所在。

更多對象數據庫阻抗專題

When to use an Embedded ODBMS

數據庫時代的終結

JavaEE/J2EE面向對象編程之道

在DDD分析設計下的JiveJdon3全新架構文檔和源碼

Hibernate等ORM使用之道

JdonRails開發視頻演示

對象數據庫db4O

ORM框架和數據庫對系統性能影響的比較

整個rmi的問題

(OO + 分佈式計算) = 軟件架構的方向


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章