由於關系數據庫的流行,很多開發者對於實體 - 關系(Entity-Relation,ER)模型非常熟悉。而 CouchDB 使用的是面向文檔(Document oriented)的模型。在使用 CouchDB 的時候,需要完成從 ER 模型到文檔模型的思維方式的轉變。下面通過幾個具體的例子來說明如何在 CouchDB 中對於一些典型的場景進行建模,並與關系數據庫中的建模方式進行比較。
描述實體
第一個場景是對實體的描述。關系數據庫中使用表來表示實體。數據庫表是有固定的模式的,該模式定義了表中每行數據應該滿足的格式。表中每行數據都對應於實體的一個實例。比如應用中如果需要描述“注冊用戶”和“圖書”兩種實體的話,就需要兩張不同的表。而在 CouchDB 中,所有的實體都是以文檔來描述的。文檔是沒有模式的,可以用任意的 JSON 對象來表示。同一實體的實例在結構上也可能不同。這更能反映問題域中數據的真實狀態。比如對“人”這一實體進行描述時,有一個字段是“傳真號碼”。因為不是所有人都擁有傳真機,這一字段是可選的。如果用關系數據庫來建模的話,則需要在表中添加一列表示傳真號碼。對於沒有傳真機的人來說,該列的值為null
。而如果用 CouchDB 中的文檔來描述的話,對於有傳真機的人,其 JSON 對象中就有一個屬性表示“傳真號碼”,否則的話就沒有此屬性。 CouchDB 強於關系數據庫的另外一個特性是可以非常容易的表示復雜數據類型。通常來說,關系數據庫中表的列只能是簡單數據類型。而 CouchDB 中的文檔由於用 JSON 來描述,可以使用任意復雜的嵌套結構。同樣是對“人”這一實體的描述,另外一個有用的信息是“家庭住址”。“家庭住址”可以簡單地用一個字符串來表示,也可以拆分成“國家”、“省(市)”、“縣”和“街道”等多個字段來表示。對於后者,如果用關系數據庫來描述的話,則需要使用多個列或是額外的表;而用 CouchDB 的文檔來描述的話,可以直接把復雜的 JSON 對象作為字段的值,如{"address" : {"country" : " 中國 ", "city" : " 北京 "}}
。比起關系數據庫來說,要更加簡單和自然。
描述一對一和一對多關系
第二個場景是描述一對一和一對多的關系。在關系數據庫中,實體之間的一對一和一對多關系是通過外鍵來描述。比如在一個電子商務應用中,訂單與其中包含的單項商品是一對一或一對多的關系。如果用關系數據庫來描述的話,需要在表示單項商品的表中添加一個字段作為外鍵,引用到訂單的主鍵。在 CouchDB 中,一般來說有兩種方式可以描述。第一種方式是把相關的實體內嵌在主文檔中。如在表示某個訂單的文檔,可以有一個字段是用來表示其中包含的單項商品,如代碼清單 5 所示。不過這種方式只適用於相關的實體數量比較少的情況,否則的話,會導致文檔過大而影響性能。另外一種方式是用分開的文檔來表示這兩種實體,並在其中一個文檔中添加一個字段,其值是另外一個文檔的 ID 。這種做法類似於關系數據庫中的外鍵引用方式。代碼清單 5 中也給出了使用這種方式表示的訂單和單項商品的文檔。
清單 5. 描述一對多關系
描述多對多關系
最后一個場景是描述多對多的關系。在關系數據庫中,實體之間的多對多關系一般是通過額外的關聯表來實現的。比如一個典型的場景是應用中“注冊用戶”與“角色”之間的關系,一個用戶可以同時具備多個角色,一個角色也可以同時有多個用戶。在關系數據庫中,用戶和角色都各自用一張表來描述,它們之間的關聯關系存放在另外一張表中,該表包含用戶和角色的外鍵引用與其它附加信息。 CouchDB 中有兩種方式來描述多對多關系。第一種類似於一對多關系中的內嵌文檔方式,只是內嵌的不是文檔本身,而只是文檔的 ID 。第二種做法類似於關系數據庫中的關聯表,使用一個額外的關聯文檔來描述關系。代碼清單 6中給出了使用這兩種做法描述用戶和角色的實例。
清單 6. 描述多對多關系