简介: 在过去数年中,开源世界的创新发展将
Java? 开发人员的生产力提升了一个层次。免费的工具、框架和解决方案弥补了曾经一度匮乏的空缺。其中,被一些人认为是
Web 2.0 数据库的 Apache CouchDB 是非常有潜力的。全面掌握 CouchDB 并不困难,使用它就像使用
Web 浏览器一样简单。本期 Java 开发 2.0 专栏将介绍 CouchDB 并向您展示如何发挥 Groovy
的 RESTClient 的强大功能
目前为止,本专栏 系列 已经探究了 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 的 通用语。参见 参考资料,获取关于
JSON 的更多信息。
试想一个违规停车罚单。该罚单将涵盖以下项目:
- 违规的日期
- 时间
- 位置
- 车辆的描述
- 牌照信息
- 违规情况
罚单上收集的格式和数据因管辖权而异。即使对于单一管辖权限内的标准停车罚单来说,它们的内容也很有可能存在不同。举例来说,警官在开罚单时可以不填写时间,或者可以省略车型,而只填写牌照的详细信息。地点可以是两条街道的组合(比如
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 实例的命令提示中,输入:
然后输入:
这些命令会自动安装 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 。
参考资料
- CouchDB:访问 Apache CouchDB 网站。
- HTTPBuilder 和 RESTClient:了解 Groovy 利用 HTTP 和 REST
的有趣且简单的方法。
- Grape:了解 Groovy 的打包引擎。
- “掌握 Ajax,第 10 部分: 使用 JSON 进行数据传输”(Brett McLaughlin,developerWorks,2007
年 3 月:全速掌握 JSON。
- MapReduce:这篇 Wikipedia 文章将阐述用于处理大数据集的 MapReduce
编程模型。
- “Java 开发 2.0:您也可以租用 EC2”(Andrew Glover,developerWorks,2009
年 9 月):了解如何开发和部署 Amazon Elastic Compute Cloud (EC2)。
- “构建 RESTful Web 服务:(Andrew Glover,developerWorks,2008
年 7 月):本教程将指导您逐步掌握 REST 的基本概念,以及如何使用 Restlet 构建应用程序。
- “Crossing borders: Concurrent programming with Erlang”(Bruce
Tate,developerWorks,2006 年 4 月):Erlang 在并发、分布式系统和软实时系统领域中的重要性日益显现。
- jcouchdb:了解 jcouchdb 项目的最新信息。
- 浏览 技术书店 获得关于各类技术专题的书籍。
- developerWorks Java 技术专区:发现数百篇关于 Java 编程各个方面的文章。
|