程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> Java開發2.0 - 通過CouchDB和Groovy的RESTClient實現REST

Java開發2.0 - 通過CouchDB和Groovy的RESTClient實現REST

編輯:關於JAVA

目前為止,本專欄 系列 已經探究了 Google 和 Amazon 平台中的雲計算。 雖然它們在實現和結構上存在不同,但兩種平台都支持快速和可擴展的部署方式 。它們可以快速經濟地組裝、測試、運行和維護 Java 應用程序,這無疑是前所 未有的。但是,雲並不是影響如今 Java 開發速度的唯一因素。開源解決方案還 可以幫助您迅速組裝軟件應用程序,因為您不再需要編寫大量代碼。手動編寫對 象關系映射(ORM)、日志或測試框架的時代已經一去不復返。這些問題已經隨 時間逐漸得到了解決,然後又再次出現在了開源領域中 — 再次面臨這些問題 — 但這些解決方案幾乎始終要比您自己的好。

關於本系列

從 Java 技術首次亮相以來,Java 開發的格局已經發生了巨大的變化。得益 於成熟的開源框架和可靠的租用式部署基礎設施,現在已經可以迅速經濟地裝配 、測試、運行和維護 Java 應用程序了。在本系列中,Andrew Glover 將探索使 這種全新開發范例成為可能的各種技術和工具。

在 Java 開發的整個過程中,開源創新簡化了應用程序的裝配過程。全新的 開源數據庫 Apache CouchDB(截至撰寫本文時的發行版為 0.10.0) 也不例外 。搭建好 CouchDB 環境之後可以輕松地使用它。您只需要操作它的 HTTP 連接 即可;既不需要 JDBC 驅動程序,也不需要第三方控制管理平台。在本文中,我 將向您介紹 CouchDB 並展示如何使用它提升開發速度。考慮到安裝的簡便性, 您將使用 Amazon 的 EC2 平台。並且,您將通過一個方便易用的 Groovy 模塊 來與它通信。

面對文檔的數據庫

關系數據庫基本上主導了數據庫市場。但其他類似的數據庫 — 包括 面向對 象 和 面向文檔 數據庫,兩者在面向關系的世界中極為不同 — 也不時發揮著 舉足輕重的作用。CouchDB 是一種面向文檔的數據庫。它是無模式的,並且允許 您以 JavaScript Object Notation (JSON) 字符串的形式來存儲文檔。

JSON

JSON 是一種輕量級的數據交換格式,同時也是 Web 應用程序的替代格式。 它類似於 XML,但遠沒有它那麼詳細。得益於其輕量級特性,它正成為 Web 的 通用語。

試想一個違規停車罰單。該罰單將涵蓋以下項目:

違規的日期

時間

位置

車輛的描述

牌照信息

違規情況

罰單上收集的格式和數據因管轄權而異。即使對於單一管轄權限內的標准停 車罰單來說,它們的內容也很有可能存在不同。舉例來說,警官在開罰單時可以 不填寫時間,或者可以省略車型,而只填寫牌照的詳細信息。地點可以是兩條街 道的組合(比如 Fourth 和 Lexington 的交匯處),或者是某個固定地址(比 如說 19993 Main Street)。但所采集信息的語義大抵相似。

罰單的數據點可以在關系數據庫中建模,但詳細信息卻有點麻煩。舉例來說 ,如何有效在關系數據庫中捕獲某個交匯點呢?並且在沒有街道交匯的情況下, 數據庫是否會使用空字段來表示第二個地址呢(假定建模方式為在各列中捕獲不 同的街道名)?

在這些情況下,關系數據庫的抽象化程度可能 會稍高一些。所需的信息已經 采用了文檔(罰單)的形式。為什麼不將數據建模為文檔呢?這樣可以不用死守 嚴格的關系模式,而只需要大致遵循高級模式的語義。這正是 CouchDB 的用武 之地。它允許您以靈活的方式來對這些域類型進行建模 — 成果是一個完備的文 檔,它沒有模式,而是使用與其他文檔大致相似的藍圖。

MapReduce

Google 獨創的 MapReduce 是一個用於處理海量數據集的概念框架。它是一 種高度優化的應用大量計算機的分布式問題解決機制。MapReduce 包含兩個函數 :map 和 reduce。map 函數用於接受大量輸入,並將它們分割為較小的部分( 同時將這些數據傳遞給其他進程)。reduce 函數的作用是將來自 map 的所有單 獨輸出整合為一個最終的輸出。

借助 CouchDB,您可以搜索文檔、文檔屬性甚至在關系世界中關聯文檔。您 的實現方式是使用視圖,而不是 SQL。從本質上說,視圖是您采用 MapReduce 樣式(在 JavaScript 中)編寫的函數;也就是說,您最終只需要編寫一個 map 函數和一個 reduce 函數。這些函數將共同過濾或提取文檔數據,或者有效利用 它們之間的關系。事實上,CouchDB 具備足夠的靈活性,只要底層文檔沒有發生 發化,它就只需要運行這些函數一次,從而加快視圖處理過程。

CouchDB 最有意思的地方是它的設計方式。CouchDB 體現了 Web 本身的基本 (也是極為成功的)概念。它公開了一組全面的 REST 式 API,允許創建、查詢 、更新和刪除文檔、視圖和數據庫。這使得 CouchDB 的使用變得非常簡單。您 不需要借助其他驅動程序或平台來開始開發:一個浏覽器便能完成所有工作。也 就是說,豐富的庫使 CouchDB 的使用變得非常簡單 — 但從內部來看,它們僅 僅是通過 HTTP 來利用 REST 式概念。

與 Web 的本質特性相類似,CouchDB 在設計時融入了大量可擴展因素。它是 使用並發編程語言 Erlang 編寫的,它支持綁定分布式、容錯、不間斷應用程序 。該語言(現已開源可用)是由 Ericsson 開發的,並在電信環境中得到了廣泛 應用。

安裝 CouchDB,雲風格

CouchDB 的安裝方法因操作系統而異。如果使 用的是 Windows®,則需要安裝 Microsoft C 編譯器 Cygwin,以及其他一 些依賴項。如果使用的是 Mac,則需要使用 Macports。但是,如果使用的是 Linux® 平台,比如說 Ubuntu,則安裝方法就不是那麼簡單了。但並非所有 人都安裝了 Ubuntu 實例吧。您是這樣嗎?

當然,您可以方便的獲取一個 Ubuntu 實例!Amazon 的 EC2 是一種相對較 為經濟的、隨需應變地 使用 Ubuntu 的一種方法。因此,只需要施展少許 EC2 魔法,您就可以迅速搭建好 CouchDB 環境;完成後,您可以關閉它(可以這樣 說)。

首先,您需要找到一個充當基本實例的 EC2 AMI。我最終決定使 用 AMI ami-ccf615a5(一個 Ubuntu 9.04 實例),它是截至撰寫本文時的最新 版本。(當您閱讀本文時,很有可能已經出現了 9.10 版本的 AMI)。使用 Eclipse 或者 AWS Management Console 啟動一個 ami-ccf615a5 實例。確保設 置了允許通過 SSH 訪問的安全策略。(雖然 CouchDB 使用 HTTP,但考慮到簡 單性,您將通過一個 SSH 通道來與它通信)。您還需要使用一個值對。(如果 需要指導,請參見本系列的前兩篇文章 “您也可以租用 EC2” 和 “使用方便的 EC2”。)

啟動了 Ubuntu 9.04 的 EC2 實例 之後,您需要為它設置 ssh。(記住,該實例大約要 1 分鐘時間才能完全啟動 ,因此請耐心等待。)舉例來說,我可以打開一個終端並使用 ssh 設置新創建 的實例,如下所示:

aglover#> ssh -i .ec2/agkey.pem root@ec2-174-129-157- 167.compute-1.amazonaws.com

我的 AMI 的 DNS 名稱是 ec2-174-129-157-167.compute-1.amazonaws.com ,並且我引用的值對的名稱是 agkey。您的 DNS 名稱和值對肯定會不一樣。

在雲 Ubuntu 實例的命令提示中,輸入:

apt-get update

然後輸入:

aptitude install couchdb

這些命令會自動安裝 CouchDB。但是,注意它們不會安裝最新版本。如果需 要最新版本,則需要通過源代碼來安裝 CouchDB。

命令執行結束後,您可以通過發出 ps -eaf 命令來檢查 CouchDB 是否已經 正常運行。通過將 ps 輸出傳遞給 egrep,查看在路徑中使用 couchdb 的進程 。您應該能看到如清單 1 所示的輸出:

清單 1. CouchDB 正在運行中(各行經過分段以適應頁面寬度)

couchdb  1820   1 0 00:54 ?    00:00:00 /bin/sh  -e /usr/bin/couchdb
   -c /etc/couchdb/couch.ini -b -r 5 -p /var/run/couchdb.pid  -o /
couchdb  1827 1820 0 00:54 ?    00:00:00 /bin/sh -e  /usr/bin/couchdb
   -c /etc/couchdb/couch.ini -b -r 5 -p /var/run/couchdb.pid  -o /
couchdb  1828 1827 0 00:54 ?    00:00:00  /usr/lib/erlang/erts-5.6.5/bin/beam
   -Bd -- -root /usr/lib/erlang -progname erl -- -home  /v
couchdb  1836 1828 0 00:54 ?    00:00:00 heart -pid  1828 -ht 11

接下來,回到本地機器,設置一個 SSH 通道來允許訪問在雲上運行的 CouchDB 實例,就像是在自己的機器上一樣。為此,在本地機器上打開一個新的 終端會話,然後輸入:

ssh -i your key -L 5498:localhost:5984 root@your AMI  DNS

最後,在本地機器上打開一個浏覽器。在地址欄中,輸入 http://127.0.0.1:5498/。您應該可以看到一個漂亮的 JSON 歡迎消息,如下所 示:

{"couchdb":"Welcome","version":"0.8.0-incubating"}

現在,看上去一切運轉正常,接下來可以開始應用 CouchDB 了。

采用 REST 風格使用 Groovy 的 RESTClient

REST

在表示狀態傳輸(REST)設計風格中,松散耦合的 Web 應用程序將依賴於指 定資源 — 比如采用統一資源定位符(URL)、統一資源標識符(URI)和統一資 源名稱(URN)的形式 — 而不是消息。REST 明智地采用了 Web 中經過驗證並 獲得了成功的基礎設施 — HTTP。也就是說,REST 將利用 HTTP 協議的各個方 面,比如 GET 和 POST 請求。這些請求將很好地滿足業務應用需求,比如創建 、讀取、更新和刪除(CRUD)。

由於 CouchDB 通過以上 REST 式 HTTP 接口來公開數據,因此使用 CouchDB (您已經在浏覽器中見識一二)是相當簡單的。幾乎所有工作都可以通過 HTTP 來完成。

您可以選擇各種工具來與 HTTP 進行交互。在使用 REST 式接口時,我比較 偏愛 Groovy HTTPBuilder 的 RESTClient 擴展。 HTTPBuilder — 針對 Apache Commons Project 的 HTTPClient 的包裝器 — 在 HTTP POST、GET、 PUT 和 DELETE 的語法中添加了一些 Groovy 因素。由於 HTTPBuilder 是使用 Groovy 創建的,因此編寫利用 REST 式概念的腳本(比如與 CouchDB 通信)都 簡單得不能再簡單了。

Grape 的進一步簡化

為了與 Java 開發 2.0 的一般主題保持一致 — 快速、簡便和免費(或便宜 ) — Groovy 的便捷的 Grape(Groovy Advanced Packaging Engine 或 Groovy Adaptable Packaging Engine)特性對於與 HTTPBuilder 這樣的交互非 常有用。Grape 是一種依賴項管理器,它允許 Groovy 腳本和類在運行時 自動 配置自己的特定依賴項。這簡化了各種開源庫的使用,因為您不需要下載各種 JAR 文件便可開始編寫代碼。舉例來說,借助 Grape,您可以編寫一個 Groovy 腳本來使用 HTTPBuilder,而不需要 HTTPBuilder 的 JAR。借助 Grape,可以 在運行時或編譯時下載它們(通過 Apache)。

您將通過注釋和方法調用來利用 Grape。舉例來說,您可以使用 @Grab 注釋 來修飾某個方法或類聲明。在該注釋中,您將主依賴項指定一些相關的元數據( 借助 Ivy 的魔力,所有中間依賴項都可以悉數確定)。在運行時或編譯時(無 論孰前孰後),Grape 將下載這些依賴項並確保它們在您的類路徑下。如果已經 下載了依賴項(比如說從之前的運行中),則 Grape 仍然會確保類路徑下包含 適當的 JAR 文件。

通過 Groovy 簡化 CouchDB 的 REST 風格

在可以在 CouchDB 中創建任意文檔之前,您必須首先創建一個數據庫。要創 建一個停車罰單數據庫,可以通過 HTTPBuilder 的域相關語言(DSL)使用其 RESTClient 來發出一個 HTTP PUT,如清單 2 所示。(本文示例中的所有 Groovy 代碼都可以從 下載 小節獲得。)

清單 2. 創建一個 CouchDB 數據庫

import static  groovyx.net.http.ContentType.JSON
import groovyx.net.http.RESTClient

@Grab(group='org.codehaus.groovy.modules.http-builder',  module='http-builder',
   version='0.5.0-RC2')
def getRESTClient(){
  return new RESTClient("http://localhost:5498/")
}

def client = getRESTClient()
def response = client.put(path: "parking_tickets",
     requestContentType: JSON, contentType: JSON)

assert response.data.ok == true : "response from server  wasn't ok"

CouchCB 應該會返回應答 {"ok":true}。如清單 2 所示,在 HTTPBuilder 中可以輕松地解析 JSON 並確保 ok 元素的值確實為 true。

接下來,我們需要創建一些文檔,以便與停車罰單的主題保持一致。要建立 一個停車罰單模型,需要記住一些與罰單相關的方面。另外需要記住,由於它們 是警官將填寫的實際表單,因此一些字段可以不填或者采用預定義的模式 — 考 慮交匯點與准確地點。

使用 HTTPBuilder,您可以通過 HTTP PUT 在 CouchDB 中創建一個文檔(就 像在清單 2 中創建數據庫一樣)。由於 CouchDB 將處理 JSON 文檔,因此您必 須遵循 JSON 的名稱值格式。為此,在 Groovy 中創建一種類似於映射的數據結 構(HTTPBuilder 會將其轉換為有效的 JSON)。如清單 3 所示:

清單 3. 通過 RESTClient創建一個 CouchDB 文檔

response =  client.put(path: "parking_tickets/1234334325", contentType:  JSON,
     requestContentType: JSON,
     body: [officer: "Kristen Ree",
         location: "199 Baldwin Dr",
         vehicle_plate: "Maryland 77777",
         offense: "Parked in no parking zone",
         date: "2009/01/31"])

assert response.data.ok == true : "response from server  wasn't ok"
assert response.data.id == "1234334325" : "the returned ID  didn't match"

清單 3 完成了多項任務。首先,在為 CouchDB 文檔發出 PUT 時,您必須分 配一個 UUID。CouchDB 可以為您分配這些值,或者您也可以自已管理它們。在 清單 3 中,我設定了一個值(1234334325);隨後,該 UUID 將附加到 URL。 如果該 UUID 可用,則 CouchDB 會為它分配執行了 PUT 操作的文檔。在 put 調用的 body 部分,注意為各名稱指定相關值的方式,幾乎與普通的映射無異。 舉例來說,指定警官的名稱為 Kristen Ree,而罰單的地點是 199 Baldwin Dr 。

清單 4 采用相同的技巧在 CouhDB 中創建了另外一個罰單:

清單 4. 另一個停車罰單

def id = new Date().time
response = client.put(path: "parking_tickets/${id}",  contentType: JSON,
     requestContentType: JSON,
     body: [officer: "Anthony Richards",
         location: "Walmart Parking lot",
         vehicle_plate: "Delaware 4433-OP",
         offense: "Parked in non-parking space",
         date: "2009/02/01"])

assert response.data.ok == true : "response from server  wasn't ok"
assert response.data.id == "${id}" : "the returned ID didn't  match"

每次通過 RESTClient 發出 PUT 時,我都斷言 JSON 應答的 ok 值為 TURE ,並且驗證是否存在 id 值。注意在清單 4 中,我沒有創建 UUID,而是使用了 當前的時間 — 並不是非常簡單的技巧,但我已經不再滿足於使用簡單的交匯點 。

在 CouchDB 中成功創建了新文檔之後,它會返回一個包含 UUID 和修訂 ID 的 JSON。舉例來說,該應答表示我在清單 4 中驗證的 JSON:

{"ok":true,"id":"12339892938945","rev":"12351463"}

您的 id 和 rev 值肯定不一樣。注意,我可以通過發出 response.data.id 這樣的調用來捕獲 id 值。

在 CouchDB 中,它將通過修訂來跟蹤文檔,因此您可以返回之前的文檔版本 (通過修訂 ID),這與 CVS 或 Subversion 中的方法極為類似。

CouchDB 中的視圖

現在,我已經創建了一些停車罰單(或者,用 CouchDB 的術語來說是一些文 檔),接下來可以在 CouchDB 中創建一些視圖了。記住,視圖就是實際的 MapReduce 函數;因此,您必須定義它們。在許多情況下,您都不需要 reduce 函數;map 函數可以幫助您完成大多數任務。正如其名,它是任務的映射。舉例 來說,您可以映射希望過濾或查找的任何 “事物” 或方面。

我已經定義了兩個罰單:一個由 Officer Ree 開示,而另一個是由 Officer Richards 發出的。舉例來說,要查找 Officer Ree 開出的所有罰單,您可以編 寫一個 map 函數來過濾相應的 officer 屬性。然後,您可以將結果傳遞給 CouchDB 的 emit 函數。

使用 CouchDB 的管理接口:Futon

您可以通過 CouchDB 的 REST 式 API 或通過 CouchDB 的管理接口 Futon 來定義視圖。Futon 僅僅是一個 Web 應用程序,可以從 http://localhost:5498/_utils/ 下載它。立即訪問該位置(假定您已經跟隨我 創建了數據庫和一些文檔),您應該能看到一個針對 parking_tickets 的簡單 接口,如圖 1 所示:

圖 1. Futon 接口

如果選擇 parking_tickets 數據庫,則隨後可以在最右側看到一個下拉列表 (Select view:)。通過選擇 Custom query...來定義一個自定義視圖,如圖所 示:

圖 2. Futon 的視圖選擇接口

現在,Futon 接口可允許您定義 map 函數和 reduce 函數。(您可能需要單 擊 查看代碼 鏈接)。在 Map 文本框中,定義如清單 5 所示的簡單映射:

清單 5. CouchDB 中的簡單的 map 函數

function(doc) {
  if(doc.officer == "Kristen Ree"){
   emit(null, doc);
  }
}

如您所見,清單 5 中的 map 函數是使用 JavaScript 定義的。它的作用是 通過文檔的 officer 屬性來過濾 CouchDB 數據庫中的文檔。特別需要說明的是 ,僅當警官的名稱為 Kristen Ree 時,該函數才會傳遞一個文檔給 emit。圖 3 顯示了在 Futon 中定義此函數的位置:

圖 3. 創建一個 MapReduce 函數

接下來,您需要指定文檔名稱(輸入 by_name)和視圖名稱(輸入 officer_ree)。這些名稱將作為建立 URL 以便稍後調用此視圖的途徑(該 URL 也就是 http://localhost:5498/parking_tickets/_view/by_name/officer_ree )。

現在,您可以通過 HTTPBuilder 來使用該視圖:

清單 6. 調用您的新視圖

response = client.get(path:  "parking_tickets/_view/by_name/officer_ree",
     contentType: JSON, requestContentType: JSON)

assert response.data.total_rows == 1

response.data.rows.each{
   assert it.value.officer == "Kristen Ree"
}

該視圖將返回一個 JSON 應答,其中只包含一個文檔:Officer Ree 於 1 月 31 日開出的罰單。通過解析相應的 JSON,清單 6 中的 response 對象將隱藏 原始 HTTP 應答。您可以通過對 response 的 data 屬性調用 toString 方法來 查看原始 JSON 應答。原始應答將如清單 7 所示:

清單 7. 視圖的原始結果

{"total_rows":1,"offset":0,"rows": [
  {"id":"1234334325","key":null,
   "value":{"_id":"1234334325","_rev":"4205717256","officer":"Kristen  Ree",
     "location":"199 Baldwin Dr","vehicle_plate":"Maryland 77777",
     "offense":"Parked in no parking zone","date":"2009/01/31"}}]}

從返回的原始 JSON 文檔中可以看出,HTTPBuilder 可以非常輕松地解析 JSON,因為它支持通過類似於對象圖的機制來估計各種屬性及其相應的值。

為便於演示,我將向數據庫再添加一些文檔。為了跟隨示例,您應該使用 代 碼下載 完成相同的任務。

CouchDB 的 emit 函數將充當各種形式的組織器。如果未在 map 函數中添加 限制(像我在 清單 5 中做的那樣),則 emit 的基本作用是對傳入文檔進行排 序。舉例來說,如果您希望按日期獲取所有罰單(此處可看作 SQL 的 ORDER BY 語句),則可以按文檔的 date 來執行 emit,如清單 8 所示:

清單 8. 一個比較簡單的 map 函數

function(doc) {
  emit(doc.date, doc);
}

清單 9 向此視圖發出了一個 HTTP GET(我已經指定 dates 作為設計文檔名 ,by_date 作為視圖名。)。

清單 9. 調用的另一個視圖

response = client.get(path:  "parking_tickets/_view/dates/by_date", contentType: JSON,
     requestContentType: JSON)
assert response.data.total_rows == 4

清單中的查詢將按日期順序返回 parking_tickets 數據庫中的所有文檔。 assert 語句僅驗證 total_rows 屬性是否等於 4。這是一個關鍵點。視圖將返 回一些結果以及少許元數據(比如說返回文檔的數量);因此,它將幫助在開始 解析之前查看原始應答。清單 10 顯示的原始結果:

清單 10. 按日期排序的原始 JSON 文檔

{"total_rows":4,"offset":0,"rows":[
  {"id":"85d4dbf45747e45406e5695b4b5796fe","key":"2009/01/30",
   "value":
   {"_id":"85d4dbf45747e45406e5695b4b5796fe","_rev":"1318766781",
    "officer":"Anthony Richards",
    "location":"54th and Main","vehicle_plate":"Virginia FCD-4444",
    "offense":"Parked in no parking zone","date":"2009/01/30"}},
  {"id":"1234334325","key":"2009/01/31",
   "value":
   {"_id":"1234334325","_rev":"4205717256",
    "officer":"Kristen Ree",
    "location":"199 Baldwin Dr","vehicle_plate":"Maryland 77777",
    "offense":"Parked in no parking zone",
    "date":"2009/01/31"}},
  {"id":"12345","key":"2009/01/31",
   "value":
   {"_id":"12345","_rev":"1479261876",
    "officer":"Anthony Richards","location":"1893 Main St",
    "vehicle_plate":"Maryland 4433-OP",
    "offense":"Parked in no parking zone","date":"2009/01/31"}},
  {"id":"12339892938945","key":"2009/02/01",
   "value":
   {"_id":"12339892938945","_rev":"12351463","officer":"Anthony  Richards",
    "location":"Walmart Parking lot","vehicle_plate":"Maine 4433- OP",
    "offense":"Parked in non-parking space",
   "date":"2009/02/01"}}]}

在定義這樣的視圖時,您可以隨後向它傳遞一個值 — 也就是 emit 函數的 初始值。舉例來說,清單 8 中定義的視圖主要用於按日期排序。如果希望按具 體的 日期,則將該日期傳遞給視圖查詢。舉例來說,可以在浏覽器的地址欄中 輸入以下 URL:

http://localhost:5498/parking_tickets/_view/dates/by_date? key="2009/01/31"

然後,這個視圖只會返回 1 月 31 日發出的罰單。您應該可以在浏覽器窗口 中看到一些 JSON 樣式的文本,它們與清單 11 中的內容相似。注意,使用浏覽 器作為查詢工具是查看 HTTP 請求的原始 JSON 應答的一種極其簡單的方式。

清單 11. 1 月 31 日僅開出了兩張罰單

{"total_rows":4,"offset":1,"rows":[
   {"id":"1234334325","key":"2009/01/31",
   "value":
    {"_id":"1234334325","_rev":"4205717256","officer":"Kristen  Ree",
     "location":"199 Baldwin Dr","vehicle_plate":"Maryland 77777",
     "offense":"Parked in no parking zone",
     "date":"2009/01/31"}},
   {"id":"12345","key":"2009/01/31",
   "value":
    {"_id":"12345","_rev":"1479261876","officer":"Anthony  Richards",
     "location":"1893 Main St","vehicle_plate":"Maryland 4433-OP",
     "offense":"Parked in handicap zone without permit",
     "date":"2009/01/31"}}]}

您可以根據需要來定制視圖的功能。舉例來說,只需要少量 JavaScript 字 符串操作,我就可以編寫一個查找 Main Street 上開出的罰單的視圖,如清單 12 所示:

清單 12. 另一個添加了字符串魔力的視圖

function(doc) {
  if(doc.location.toLowerCase().indexOf('main') > 0){
   emit(doc.location, doc);
  }
}

可以從清單 12 中看出,如果任何文檔的 location 元素包含 main,則該文 檔將被傳遞給 emit 函數。記住,這種搜索相當廣泛。如果某文檔的 location 包含一個 Germaine Street 這樣的字符串,則會返回它。對於我所定義的少量 罰單,視圖將返回如清單 13 所示的結果:

清單 13. 按 Main Street 過濾的結果

{"total_rows":2,"offset":0,"rows":[
  {"id":"123433432asdefasdf4325","key":"4th and Main",
   "value":
   {"_id":"123433432asdefasdf4325","_rev":"498239926",
   "officer":"Chris Smith","location":"4th and Main",
    "vehicle_plate":"VA FGA-JD33",
    "offense":"Parked in no parking zone","date":"2009/02/01"}},
  {"id":"123433432223e432325","key":"54 and Main",
   "value":
   {"_id":"123433432223e432325","_rev":"841089995",
   "officer":"Kristen Ree","location":"54 and Main Street",
    "vehicle_plate":"Maryland 77777",
    "offense":"Parked in no parking zone","date":"2009/02/02"}}]}

注意,JSON 應答包含一個 key 元素,用於描述為何要生成特定的文檔。這 一方面的信息相當有用。還需注意,我所定義的各種罰單中的數據在一致性方面 做的不是很好:一些地點是准確的,但也有一些是不精確的。雖然該數據可以存 儲在關系數據庫中,但也同樣可以使用面向文檔的模型。另外,借助 Groovy 和 HTTPBuilder 的高效解析 JSON 的強大能力,可以非常輕松地獲取數據(比原始 JDBC 簡單很多)。

CouchDB 作為 Web 數據庫

CouchDB 的迷人之處在於它的使用非常簡單。關系數據庫也很簡單,但該數據庫的優勢在於您只需要熟悉 Web 浏覽器就可以掌握其 API。此外,由於 CouchDB 提供了 REST 式 API,您可以通過一些炫酷的框架來與其通信,比如說 HTTPBuilder 的 RESTClient。您也不會受制於 HTTPBuilder;各種 Java 庫都 在嘗試簡化 CouchDB 的使用。其中非常有潛力的一個就是 jcouchdb,它可以幫 助您避免 REST 化和 JSON 化,同時允許您以編程方式使用 Java 語言操作文檔 和視圖。

敬請期待下個月的專欄文章,我將回過頭來討論 Google App Engine。在開 放創新精神的大旗下,各種新框架不斷湧現,旨在促進 Google App Engine 的 開發和部署。您將了解它們如何能夠簡化 Google 雲平台上的 Java 開發 2.0 。

本文配套源碼

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved