用 Project Zero 的数据访问 API 构建简单 wiki
本文假设您对 Project Zero 已基本了解。要想加快进度,不妨先阅读一下介绍性文章 “为 Web 应用程序构建 RESTful 服务” 和介绍性教程 “用 Project Zero 开发应用程序: Project Zero 和 PHP 入门”。熟悉了 Project Zero 之后,就可以下载它(参见 参考资料)并编写简单的应用程序。
Larry Wall 的那句话真棒:让容易的事情保持容易,让困难的事情变得可能。
数据访问方面的用例既有简单的也有非常复杂的。因此,很多开发人员都希望获得一种足够灵活的数据访问 API,这种 API 既能处理复杂情形,又能使简单情形不会变得过于复杂。Project Zero 的数据访问 API(称为 zero.data)恰好能满足这个需求。它不是一种类似于 Hibernate 或 Java™ Persistence Architecture 的抽象层,而是一种库,力求通过提供一种围绕 SQL 的瘦包装器来 “让容易的事情保持容易,让困难的事情变得可能”,本文在稍后将会阐述。例如,清单 1 显示了如何执行一个简单的查询并获得由一列 bean 实例组成的结果:
清单 1. 使用 zero.data 返回一列 bean 和映射实例
在过去的一年中,Project Zero 团队一直与 IBM® 的 Information Management 开发人在 pureQuery 技术上进行紧密合作,pureQuery 是一种高性能的数据访问平台,包括开发工具、API 和面向 Java 应用程序的高级运行时。zero.data 利用了 pureQuery 的 API 和运行时。zero.data 也支持使用 pureQuery 的工具集,因为 pureQuery 工具能生成 Java 工件。但在写作本文时,还没有该健壮工具集的特定于 Project Zero 的集成(有关 pureQuery 的更多信息,可以参考 参考资料)。 图 1 显示了 Project Zero 的 API 和 pureQuery 之间的关系:
图 1. Project Zero 的 zero.data 关系数据访问 API 之间的关系
|
zero.data 围绕 pureQuery API 和运行时的功能提供了一种瘦包装 API。如果要在 Project Zero 应用程序中支持某些简化的假设(比如,配置和连接池化),以及提供公用的 API 以便 Groovy 和 PHP 脚本都能以对各自语言有益的方式利用健壮的 API,那么使用它要比使用 pureQuery API 更合适。比如,在 Groovy 接口中公布的一些方法都使用闭包,而其 PHP 对等物则使用 PHP 资源标识符。
本文主要介绍 Groovy API。有关 Java 和 PHP API 的更多信息,可以参阅 Project Zero 文档。在本文的剩余部分,您会了解在 Zero 应用程序中使用 zero.data 的基本知识。首先,您将学习使用 zero.data.groovy.Manager
接口管理数据,并了解其设计背后的动机,包括从 Java 和 Groovy 两个角度来看待 API 。接下来,您会立即开始实践,创建一个应用程序来使用 zero.data API。应用程序创建之后,将会构建一个简单 wiki、初始化表和最终完成该应用程序的代码。大概步骤如下所示:
zero.data.groovy.Manager
(以下均简称为 Manager
)定义了一种方便的 API,用户可使用它查询和操纵关系数据库。此 API 最基本的用法是传递 SQL 字符串。在 Groovy,传入的是嵌入在字符串内的参数,如下所示:data.queryFirst("SELECT * FROM t WHERE id=$id")
。 Manager
然后使用 PreparedStatement
准备此字符串、执行它并返回结果。它还能智能地管理所创建的相关资源(closing resource):比如结果集、语句和连接。
Manager
提供了一系列方法来执行如下操作:
- 执行查询并在每个执行闭包的行上进行迭代。
- 执行查询并只返回已转变成如下形式的 ResultSet 的第一行:
- Java Bean 类的实例
- java.util.Map
- 执行查询并通过以下形式返回 ResultSet 中转变为上述形式之一的所有行:
- java.util.Iterator
- java.util.List
- Java 数组
- 执行查询并返回 ResultSet。
- 执行数据操纵语言语句(比如 INSERT、UPDATE 和 DELETE),包括一种针对生成的键的特殊方法。
就数据而言,很有必要看看 Manager
如何减少样板代码,以及使您专注于功能而不是包含很多 bug 的代码。例如,清单 2 展示了 清单 1 所示代码的纯 JDBC 实现,展示了 Manager
如何显著地让简单的事情变得容易:
清单 2. 使用 JDBC 返回 Map 的一个实例更为繁琐
实际上,应用程序代码很少像 清单 2 所示的那样直接利用 JDBC。大多数框架都在某种程度上提供了一个间接和简化层。我们相信 Manager
能够很大程度地让简单的事情变得容易,让困难的事情成为可能。比如,除了能返回单一实例或 Java bean 类或 Map 的 List、Iterator 或 Array(如 清单 1 所示)之外,zero.data 还提供了一个模板方法途径,允许开发人员或可能的第三方实现复杂 ResultSet
处理。
模板方法途径的更高级的用法将会在后续的 developerWorks 文章中讨论。正如之前提到的,zero.data 构建在 pureQuery 运行时引擎之上。pureQuery 运行时不仅提供了一种框架,通过一种开放 API 以可预见的灵活方式约束对数据库的访问,而且还鼓励代码的高度重用。比如,pureQuery 运行时本身主要由一些接口的实现组成,而这些接口是开发人员为了扩展该引擎而实现的。这样,pureQuery 运行时就能足够灵活地处理任何情况,从针对某个给定应用程序而对其引擎进行一次性扩展到采用可重用的 ResultSet
处理组件库的机构。
如 图 1 所示,Manager
实际上包装了 zero.data.Manager
,提供了更多的 Groovy 友好的捷径。Manager
的数据访问 API 和 zero.data.Manager
的 API 的比较如下。您将会注意到二者都具有共同的主题。也就是说,很多方法类型完成的任务几乎相同,只不过针对特殊的用例。比如,queryList
返回 java.util.List
,而 queryIterator
返回 java.util.Iterator
。这很容易理解。这些方法类型中的每一个都具有三个重载方法,这些方法类型中的每一个都具有三个重载方法,这些方法类型中的每一个都具有三个重载方法,而且在签名和函数上是对称的。表 1 列出了所有的方法类型和重载方法,以及相关描述。可以在 Project Zero 的 Javadoc API 文档中找到更多信息。
表 1.
Manager
方法的高层概览方法 | 描述 | 变量 | Groovy 中的对等物 |
---|---|---|---|
queryFirst | 返回 ResultSet 中的第一行。 |
Map<String,Object> queryFirst(String, Object...) | Map<String,Object> queryFirst(GString) |
T queryFirst(String, Class<T>, Object...) | T queryFirst(GString, Class<T>) | ||
T queryFirst(String, RowHandler<T>, Object...) | T queryFirst(GString, RowHandler<T>) | ||
queryList | 将 ResultSet 作为 java.util.List 返回。 |
List<Map<String,Object>> queryList(String, Object...) | List<Map<String,Object>> queryList(GString) |
List<T> queryList(String, Class<T>, Object...) | List<T> queryList(GString, Class<T>) | ||
List<T> queryList(String, RowHandler<T>, Object...) | List<T> queryList(GString, RowHandler<T>) | ||
queryIterator | 将 ResultSet 作为 java.util.Iterator 返回。 |
Iterator<Map<String,Object>> queryIterator(String, Object...) | Iterator<Map<String,Object>> queryIterator(GString) |
Iterator<T> queryIterator(String, Class<T>, Object...) | Iterator<T> queryIterator(GString, Class<T>) | ||
Iterator<T> queryIterator(String, RowHandler<T>, Object...) | Iterator<T> queryIterator(GString, RowHandler<T>) | ||
queryArray | 将 ResultSet 作为 Java 数组返回。 |
Map<String,Object>[] queryArray(String, Object...) | Map<String,Object>[] queryArray(GString) |
T[] queryArray(String, Class<T>, Object...) | T[] queryArray(GString, Class<T>) | ||
T[] queryArray(String, RowHandler<T>, Object...) | T[] queryArray(GString, RowHandler<T>) | ||
update | 执行更新并返回受影响的行数。 | int update(String, Object...) | int update(GString) |
insert | 执行并返回所生成的键值,该键值已强制转型成在第二个参数中指定的类。Groovy 的对等物则将其强制转型成 int 。 |
T insert(String, Class<T>, String[], Object...) | int insert(GString, List<String>) |
正如 表 1 所展示的,zero.data 具有一个全面的数据访问方法集合,足以适应各种情况。我们只使用 zero.data.groovy.Manager
版的 queryFirst(GString)
、queryList(GString)
、 update(GString)
和 insert(GString, List)
。
Groovy 是一种运行于 Java 平台上的面向对象的动态编程语言。有一种误解认为 Groovy 试图取代 Java 编程语言。由于 Groovy 会编译成 Java 字节码,所以它能利用标准的 Java API 和很多用 Java 语言编写的库。因此,Groovy 无意取代 Java 语法,而是提供一种包含很多语言结构的语法作为 Java 语法的补充,从而使代码更简洁紧凑。
比如,正如之前所演示的,使用 Java 版的 Project Zero 的 zero.data API,可以减少标准 JDBC 方法调用中的代码行。清单 3 对功能上等价的 Java 和 Groovy 代码进行了比较:
清单 3. 功能上等价的 Java 和 Groovy 代码的对比
从清单 3 可以看出,不仅代码行数减少了,而且 Groovy 语言似乎也显得比较灵活。比如,在 Java 版的 inTransaction
方法中,我们必须显式地创建 LocalTransaction
的一个匿名实例。但是,在 Groovy 中,我们将此隐藏于闭包之后。闭包是一种在 Groovy 内创建 “可移植” 代码的方法。与用 Java 语言创建抽象 LocalTransaction
类的匿名实现并将其传递给 inTransaction
方法类似,在 Groovy 中,可以以一种更为简洁的方式对闭包大括号(“{” 和 “}”)间的代码进行压缩并将其发送到 inTransaction
方法。Groovy 文档(参见 参考资料)对 Groovy 中的闭包进行了更为全面的解释,包括示例以及如何编写将闭包作为实参的 API。
清单 3 中的代码的 Groovy 版给出了对闭包的第二和第三次使用。eachRow
方法可以接受闭包,并执行每一行结果中的大括号之间的代码。正如从清单中可以看到的,该代码调用了另一个闭包,此闭包只对行 Map 中的条目进行循环并打印输出键和值。
|
在本文的剩余部分,将使用典型的 Model-View-Controller 模式创建一个简单的 wiki,从而让您领略到 zero.data 所能提供的强大功能。图 2 展示了在 Zero 应用程序中实现这一目的的高层架构;在本文稍后,我们将介绍在何处放置代码工件以支持 Zero 约定。
图 2 展示了 Project Zero 应用程序中的 Model-View-Controller 实现:
图 2. Project Zero 应用程序中的 Model-View-Controller 实现
要启动您的应用程序,首先需要创建一个 Project Zero 应用程序。后续的图展示了针对 Eclipse 的 Project Zero 插件的使用,这些插件提供了开发应用程序的用户界面捷径(但也可以使用命令行实用工具的类似功能实现同样的目的)。
首先,必须创建一个 Zero 应用程序。"Project Zero 简介,第 1 部分" 给出了使用 Project Zero Eclipse 用户界面创建应用程序的详细步骤,此外,还详细介绍了构成 Project Zero 应用程序的各种文件夹和工件。但是此处只介绍一些基础步骤。
单击 File > Project.. 菜单项创建应用程序。在随后出现的对话框中,可以选择项目向导。选择 Project Zero > Project Zero Application 启动向导,如图 3 所示:
图 3. 选择 Project Zero Application 向导
向导的第一个屏幕提示输入应用程序名称。该名称可以任意命名,如图 4 所示,我们将其命名为 mywiki:
图 4. New Project Zero Application 名称提示
这会在 Project Zero 应用程序文件夹结构的 Eclipse 工作区创建一个项目和一些默认的应用程序工件。找到 mywiki 项目。它应该如图 5 所示:
由于 Project Zero 使用了 Ivy 框架来维持依赖项,因此很容易获得 zero.data。依赖项在应用程序的 ${app_home}/config/ivy.xml
中声明。为了简便起见,我们使用嵌入模式的 Apache Derby 进行演示。对此文件进行编辑使其包括清单 4 中给出的那两行 XML:
参考 “Project Zero 简介,第 1 部分” 对新的依赖项进行解析,然后再返回这里。
zero.data 使用 javax.sql.DataSource
连接到数据库。可以在 Project Zero 应用程序的 zero.config 文件中配置连接属性,从而配置并连接到一个或多个数据库。清单 5 给出了如何使用 “wiki” 键配置给定的数据库:
清单 5. 在 zero.config 中配置 zero.data
配置好数据库连接属性之后,就可以获取 Manager
实例了。利用这个实例,可以使用之前介绍的简便方法执行查询并操纵数据。我们使用在 zero.config 文件中选择的键获取经过配置的 Manager
。在本例中,我们选择了 'wiki'。将它作为实参传递给 create
方法,如清单 6 所示:
def data = zero.data.groovy.Manager.create('wiki') |
正如从 清单 4、清单 5 和 清单 6 中看到的,仅需非常少的操作就能启动并运行 Zero 应用程序。接下来,我们将定义一些基本要求并将其转换为代码,从而构建一个简单的 wiki。
为了说明 zero.data 的使用,我们将构建一个简单的 wiki 应用程序。在其最简单的形式中,wiki 创建、编辑、链接并呈现 wiki 页面。因此,在这个示例中,我们将说明规范 CRUD 操作中的 Create、Retrieve 和操作(但未说明 Delete 操作)。
为什么要实现一个 wiki 来说明 zero.data API 呢?首先,wiki 非常简单。因此,我们可以将重点始终放在 zero.data API 上,而不用将过多精力花在实现复杂 wiki 所需的细节上。其次,wiki 提供了一个很好基础,以后可以在此基础上构建额外的功能以演示更高级的 zero.data 用法以及 Project Zero 的其他特性。
|
如前面提到过的,一个基本的 wiki 实现需要创建、检索并更新 wiki 页面。大多数 wiki 实现都有一个共同点,那就是 wiki 页面首先要通过链接到其他页面来创建。wiki 用户单击这样的一个链接,会出现一个表单,提示用户创建 此页面。如果这个页面已经存在,它就会与一个链接共同呈现,此链接允许用户在与创建表单类似的表单中编辑这个页面。
我们将结合使用 Project Zero 的脚本和模板特征,并利用一些熟悉的 MVC 模式技巧将此实现原型化。也就是说,我们将会把控制器脚本放到应用程序的公共文件夹中。可以通过 URI 访问这些脚本 — 类似于 PHP 应用程序。文件系统的文件夹层次结构及文件与 URI 结构相匹配。我们的视图保存于应用程序的 /app/views 文件夹。由于要在 Groovy 中进行实现,因此控制器都会具有 .groovy 后缀,并且视图也会被加上 .gt 后缀。
为了更好地了解目前的进度,图 6 显示了创建应用程序工件后将会得到的应用程序目录。
图 6. 完成这些步骤后的 Project Zero 应用程序文件夹结构
需要了解的背景知识已经足够了,现在我们开始研究代码。
在深入介绍控制器和视图实现之前,我们需要一个用来检索、显示和操纵数据的模型。我们将在一个数据库表中保持 wiki 页面。为简单起见,我们的模型只是映射到数据库表中的某一行的一个 Map(如果不这样,那么这就不是一篇侧重介绍 zero.data 的文章了)。我们对存储的需求不大。只需要页面的名称和实际页面内容。如清单 7 中(放在 setup.sql 文件内)给出的 SQL 代码片段所示:
与前面讨论过的其他应用程序工件不同,这里没有关于 setup.sql 的约定。包括在此文件中的数据定义由一个启动处理程序执行 — 提供给 Project Zero 应用程序的代码,在应用程序启动之后的某个阶段执行。
首先,我们创建当 Project Zero 应用程序启动时将要执行的 Groovy 脚本。如果数据库不存在,我们希望该脚本执行在 setup.sql 中找到的语句。我们将采用一个开发时的小技巧,但请不要在产品环境中使用。目前还不支持实时修改现有的数据库模式。因此,如果应用程序的模式改变了,必须使用启动处理程序将此数据库从文件系统中删除。为了演示的需要,我们首先演示最佳实践。但这个简单的实现足以说明 zero.data API 的用法(参见清单 8):
清单 8. app/scripts/startup.groovy 的内容
要在启动时执行这个脚本,还需要进行相关的配置。方法是在 config/zero.config 文件中将此脚本作为启动处理程序注册,如清单 9 所示:
清单 9. 将以下内容添加到 config/zero.config
/config/handlers += { "events" : "start", "handler" : "startup.groovy" } |
这个注册告诉 Project Zero 运行时只要 “start” 事件被激发,无论处于何种情况都要执行 startup.groovy 脚本。启动 Project Zero 应用程序,现在 CREATE TABLE 语句将被执行。因为数据库尚不存在,嵌入式 Derby 将在磁盘上创建一个数据库(因为在之前配置连接属性时,设定了当数据库不存时就创建一个数据库)。
让应用程序保持运行。Project Zero 是动态的并且可以检测到绝大多数更改。现在,表单已经创建了,可以开始为控制器和视图编写代码了。让我们从呈现一个页面开始。
要呈现一个页面,我们需要一个 hook,Project Zero 运行时利用它将控制权转交给 wiki 应用程序。我们将以一个控制器的形式在 app/public 目录中实现这一目的。我们将调用呈现功能 “view.groovy”。首先,需要一个 Manager
执行页面查询。接下来,我们将执行一个查询来检索数据库中的某一行。如果此行存在,我们就呈现它。如果不存在,就显示一个创建页面。此实现如清单 10 所示:
我们现在需要创建 'view.gt' 与 'create.gt' 视图来支持 view.groovy 控制器。两个视图均使用 Groovy 模板在请求范围呈现数据(参见清单 11 和 12):
我们在前面的章节中创建了两个视图:一个被呈现的 wiki 页面和一个创建 wiki 页面的表单。为了支持这些视图,还需要创建另外的控制器和视图。也就是说,view.gt 视图链接到 edit.groovy 控制器,create.gt HTTP 通过呈现 edit.gt 将其表单 POST 给 save.groovy。edit.groovy 检索数据并显示一个与创建表单类似的表单,只不过,这里的表单是用已存在的 wiki 页面内容填充的。save.groovy 向数据库表中插入一行或更新一个已存在的行。清单 13 到 15 显示了它们的实现:
您现在拥有了一个基本的 wiki 应用程序。由于它非常简单,所以必须提供一个不存在的 wiki 页面引导应用程序。输入 http://localhost:8080/view.groovy?name=HomePage
可以实现此操作,从而得到图 7 所示的页面:
系统提示页面不存在。这很好,因为它说明应用程序正在工作。我们可以给 URI 的名称参数任意赋值,比如 “HomePage” 就不错。接下来,需要为主页创建内容。输入任意 HTML。还可以在表单中添加一个链接到其他页面(即使我们知道该页面并不存在,也可以添加)的链接,如图 8 所示:
现在,我们单击 Save Page 按钮将页面保存到数据库。然后重定向以查看我们刚刚编辑过的页面,如图 9 所示:
如果链接到了一个不存在的页面(这很容易发生,因为此时数据库中只有一个页面),那么单击此链接,就会再次跳转到缺失页面屏幕。您可以为所链接的页面创建一个页面,如图 10 所示:
以上就是有关创建简单 wiki 应用程序的全部内容。自然,我们的 wiki 还可以做更多事情。比如,可以用诸如 Markdown 或 Textile 这样的标记文本简化内容的创建和链接,以及其他操作。(您可以在以后将这些作为练习!)
Project Zero 是一个简化的开发平台,侧重于遵从 SOA 的 Web 2.0 应用程序的敏捷开发。在诸多 Project Zero 库中包含一个简化的 API,可用来执行 SQL 查询。通过本文,学习了如何利用 API 构建简单的 wiki。
我们讨论了 zero.data API 背后的机制,包括它对 pureQuery API 的包装。我们还深入探讨了 zero.data.Manager
,并给出了 Java 和 Groovy 版的 API 间的高层次的差异。然后通过使用 zero.data API 创建应用程序进行实践,包括构建一个简单 wiki、初始化数据库表,以及最终对该实现进行编程。希望本文会对您有所帮助,并且会激发您尝试创建 Zero 应用程序的兴趣!活跃的 Project Zero 社区 会在您需要的时候为您提供帮助。
描述 | 名字 | 大小 | 下载方法 |
---|---|---|---|
Project Zero Eclipse 示例项目 | wa-pz-wiki.zip | 10KB | HTTP |
关于下载方法的信息 |
学习
- 您可以参阅本文在 developerWorks 全球站点上的 英文原文 。
- “Project Zero 简介,第 1 部分: 为 Web 应用程序构建 RESTful 服务”(developerWorks,2007 年 10 月)对 Project Zero 在创建、编译和部署功能强大的 Web 应用程序方面的创新进行了总体介绍。
- “ 保持 Project Zero 应用程序的安全性,第 1 部分: 身份验证和授权”(developerWorks,2007 年 11 月)讨论了 Project Zero 是如何让应用程序资源基于访问控制的安全性变得如此简单的。
- “ 将 Active Content Filtering 用于 Project Zero 应用程序的安全性”(developerWorks,2007 年 11 月)介绍了如何防御常见的基于 Web 2.0 的应用程序攻击(比如跨站点脚本编写),以及如何使用 Active Content Filtering 提高 Project Zero 应用程序的安全性。
-
IBM Data Studio pureQuery Runtime 是一个开发工具、应用程序运行时 API 和管理 Java 数据库开发生命周期的引擎的健壮组合。Project Zero 利用运行时 API 和引擎提供灵活的 Web 2.0 应用程序开发环境。
-
Groovy Closures 展示了在 Groovy 中利用此高级语言结构的一些实用方法。
- “Template method pattern”(Wikipedia,2007 年 11 月)讨论了这种模式,以及软件库设计人员如何在设计 API 中使用它来支持用户向系统中注入新的功能,而且无需修改现有的服务。
- “优化 Project Zero 应用程序的数据库配置和依赖项”(developerWorks,2007 年 9 月)展示了管理跨多个 Project Zero 应用程序的数据库配置的技术。
-
zero.data.Manager
Javadoc API 更详细地描述了可用于数据库访问的所有方法。
- 加入 Project Zero 社区 并了解有关于 Project Zero 的方方面面。
- 有关 Project Zero 的介绍性教程 “用 Project Zero 开发应用程序: Project Zero 和 PHP 入门” 可以让您立即开始开发 Project Zero 应用程序。
- 浏览 developerWorks Web 开发专区 获得工具、代码和资源以便立即开始开发 Web 2.0 应用程序。
- developerWorks Ajax 资源中心 包含面向各种技能级别读者的信息,可帮助您将 Ajax 构建到自己的应用程序中并显著提高用户的 Web 体验。