`
zhangziyangup
  • 浏览: 1074623 次
文章分类
社区版块
存档分类
最新评论

用 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 和映射实例

Managerdata=Manager.create("myDb");
List
<Person>results=data.queryList("SELECT*FROMperson",Person.class);
List
<Map<String,Object>>resultsMap=data.queryList("SELECT*FROMperson");

在过去的一年中,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 之间的关系
Project Zero 的 zero.data 关系数据访问 API 之间的关系

Project Zero 社区
Project Zero Web 站点 中随意浏览,查看 Project Zero 如何为现代 Web 应用程序提供功能强大 — 但极其简单的 — 开发和执行平台。活跃的 社区 讨论项目开发并为开发人员提供帮助,并且欢迎您提出自己的看法!

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),包括一种针对生成的键的特殊方法。

简单 API

就数据而言,很有必要看看 Manager 如何减少样板代码,以及使您专注于功能而不是包含很多 bug 的代码。例如,清单 2 展示了 清单 1 所示代码的纯 JDBC 实现,展示了 Manager 如何显著地让简单的事情变得容易:


清单 2. 使用 JDBC 返回 Map 的一个实例更为繁琐

//assumesConnectionhasalreadybeeninitialized
//seeJDBCdocumentationformoredetails

List
<Map<String,Object>>results=newArrayList<Map<String,Object>>();
try...{
PreparedStatementstmt
=
connection.prepareStatement(
"SELECT*FROMperson");
ResultSetrs
=stmt.executeQuery();
try...{
while(rs.next())...{
ResultSetMetaDatameta
=rs.getMetaData();
intnumColumns=meta.getColumnCount();
Map
<String,Object>row=newHashMap<String,Object>(numColumns);
for(inti=1;i<=numColumns;i++)...{
row.put(meta.getColumnName(i).toLowerCase(),rs.getObject(i));
}

results.add(row);
}

}
finally...{
rs.close();
}

}
finally...{
stmt.close();
}

实际上,应用程序代码很少像 清单 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 queryList queryIterator queryArray update insert
返回 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>)
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>)
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>)
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>)
执行更新并返回受影响的行数。 int update(String, Object...) int update(GString)
执行并返回所生成的键值,该键值已强制转型成在第二个参数中指定的类。Groovy 的对等物则将其强制转型成 int T insert(String, Class<T>, String[], Object...) int insert(GString, List<String>)

Java 编程和 Groovy

正如 表 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 代码的对比
//Java
Managerdata=zero.data.Manager.create("wiki");
data.inTransaction(
newLocalTransaction()...{
publicvoidexecute()...{
data.update(
"UPDATEmytableSETname=?WHEREid=?",1,"newname");
data.insert(
"INSERTINTOanothertable(name)VALUES(?)","anothername");
List
<Map<String,Object>>results=data.queryList("SELECT*FROMsometable");
for(Map<String,Object>row:results)...{
for(Stringkey:row.keySet())...{
System.out.println(
"key:"+key+",value:"+row.get(key));
}

}

}

}
);

//Groovy
defdata=zero.data.groovy.Manager.create("wiki")
data.inTransaction
...{
data.update(
"UPDATEmytableSETname=$nameWHEREid=$id")
data.insert(
"INSERTINTOanothertable(name)VALUES($name)")
data.eachRow(
"SELECT*FROMsometable")...{row->
row.each
...{key,value->
println(
"key:$key,value:$value")
}

}

}

从清单 3 可以看出,不仅代码行数减少了,而且 Groovy 语言似乎也显得比较灵活。比如,在 Java 版的 inTransaction 方法中,我们必须显式地创建 LocalTransaction 的一个匿名实例。但是,在 Groovy 中,我们将此隐藏于闭包之后。闭包是一种在 Groovy 内创建 “可移植” 代码的方法。与用 Java 语言创建抽象 LocalTransaction 类的匿名实现并将其传递给 inTransaction 方法类似,在 Groovy 中,可以以一种更为简洁的方式对闭包大括号(“{” 和 “}”)间的代码进行压缩并将其发送到 inTransaction 方法。Groovy 文档(参见 参考资料)对 Groovy 中的闭包进行了更为全面的解释,包括示例以及如何编写将闭包作为实参的 API。

清单 3 中的代码的 Groovy 版给出了对闭包的第二和第三次使用。eachRow 方法可以接受闭包,并执行每一行结果中的大括号之间的代码。正如从清单中可以看到的,该代码调用了另一个闭包,此闭包只对行 Map 中的条目进行循环并打印输出键和值。

创建应用程序

本文中使用的 zero.data API
Project Zero 将 Groovy 用作一种能执行但无需静态、编译时类型检查的动态语言。因此,本文中使用的是 Map 版的 API,这个版本将 ResultSet 中的一行映射到一个 java.util.Map,其中的列名是 Map 中的键,而值则是此行中的值。

在本文的剩余部分,将使用典型的 Model-View-Controller 模式创建一个简单的 wiki,从而让您领略到 zero.data 所能提供的强大功能。图 2 展示了在 Zero 应用程序中实现这一目的的高层架构;在本文稍后,我们将介绍在何处放置代码工件以支持 Zero 约定。

Model-View-Controller 模式

图 2 展示了 Project Zero 应用程序中的 Model-View-Controller 实现:


图 2. Project Zero 应用程序中的 Model-View-Controller 实现
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 向导
选择 Project Zero Application 向导

向导的第一个屏幕提示输入应用程序名称。该名称可以任意命名,如图 4 所示,我们将其命名为 mywiki


图 4. New Project Zero Application 名称提示
New Project Zero Application 名称提示

这会在 Project Zero 应用程序文件夹结构的 Eclipse 工作区创建一个项目和一些默认的应用程序工件。找到 mywiki 项目。它应该如图 5 所示:


图 5. Project Zero 应用程序文件夹结构
Project Zero 应用程序文件夹结构

由于 Project Zero 使用了 Ivy 框架来维持依赖项,因此很容易获得 zero.data。依赖项在应用程序的 ${app_home}/config/ivy.xml 中声明。为了简便起见,我们使用嵌入模式的 Apache Derby 进行演示。对此文件进行编辑使其包括清单 4 中给出的那两行 XML:


清单 4. 添加到应用程序的 ivy.xml

<dependencyorg="zero"name="zero.data"rev="1.0+"/>
<dependencyorg="org.apache.derby"name="derby"rev="10.3.1.4"/>

参考 “Project Zero 简介,第 1 部分” 对新的依赖项进行解析,然后再返回这里。

配置 zero.data

zero.data 使用 javax.sql.DataSource 连接到数据库。可以在 Project Zero 应用程序的 zero.config 文件中配置连接属性,从而配置并连接到一个或多个数据库。清单 5 给出了如何使用 “wiki” 键配置给定的数据库:


清单 5. 在 zero.config 中配置 zero.data

/config/db/wiki={
"class":"org.apache.derby.jdbc.EmbeddedDataSource",
"databaseName":"db/wiki",
"connectionAttributes":"create=true"
}

获取 Manager 实例

配置好数据库连接属性之后,就可以获取 Manager 实例了。利用这个实例,可以使用之前介绍的简便方法执行查询并操纵数据。我们使用在 zero.config 文件中选择的键获取经过配置的 Manager。在本例中,我们选择了 'wiki'。将它作为实参传递给 create 方法,如清单 6 所示:


清单 6. 获取经过配置的 Manager

                
def data = zero.data.groovy.Manager.create('wiki')

正如从 清单 4清单 5清单 6 中看到的,仅需非常少的操作就能启动并运行 Zero 应用程序。接下来,我们将定义一些基本要求并将其转换为代码,从而构建一个简单的 wiki。

构建一个简单的 wiki

为了说明 zero.data 的使用,我们将构建一个简单的 wiki 应用程序。在其最简单的形式中,wiki 创建、编辑、链接并呈现 wiki 页面。因此,在这个示例中,我们将说明规范 CRUD 操作中的 Create、Retrieve 和操作(但未说明 Delete 操作)。

为什么用 wiki?

为什么要实现一个 wiki 来说明 zero.data API 呢?首先,wiki 非常简单。因此,我们可以将重点始终放在 zero.data API 上,而不用将过多精力花在实现复杂 wiki 所需的细节上。其次,wiki 提供了一个很好基础,以后可以在此基础上构建额外的功能以演示更高级的 zero.data 用法以及 Project Zero 的其他特性。

wiki 是什么?
wiki 是一个允许用户快速、容易地创建、编辑和链接 Web 页面的软件。这个术语源于一个夏威夷词,意思是快捷非正式的

Wiki 需求

如前面提到过的,一个基本的 wiki 实现需要创建、检索并更新 wiki 页面。大多数 wiki 实现都有一个共同点,那就是 wiki 页面首先要通过链接到其他页面来创建。wiki 用户单击这样的一个链接,会出现一个表单,提示用户创建 此页面。如果这个页面已经存在,它就会与一个链接共同呈现,此链接允许用户在与创建表单类似的表单中编辑这个页面。

我们将结合使用 Project Zero 的脚本和模板特征,并利用一些熟悉的 MVC 模式技巧将此实现原型化。也就是说,我们将会把控制器脚本放到应用程序的公共文件夹中。可以通过 URI 访问这些脚本 — 类似于 PHP 应用程序。文件系统的文件夹层次结构及文件与 URI 结构相匹配。我们的视图保存于应用程序的 /app/views 文件夹。由于要在 Groovy 中进行实现,因此控制器都会具有 .groovy 后缀,并且视图也会被加上 .gt 后缀。

为了更好地了解目前的进度,图 6 显示了创建应用程序工件后将会得到的应用程序目录。


图 6. 完成这些步骤后的 Project Zero 应用程序文件夹结构
完成这些步骤后的 Project Zero 应用程序文件夹结构

需要了解的背景知识已经足够了,现在我们开始研究代码。

初始化表

在深入介绍控制器和视图实现之前,我们需要一个用来检索、显示和操纵数据的模型。我们将在一个数据库表中保持 wiki 页面。为简单起见,我们的模型只是映射到数据库表中的某一行的一个 Map(如果不这样,那么这就不是一篇侧重介绍 zero.data 的文章了)。我们对存储的需求不大。只需要页面的名称和实际页面内容。如清单 7 中(放在 setup.sql 文件内)给出的 SQL 代码片段所示:


清单 7. setup.sql 的内容

CREATETABLEpages(
id
intNOTNULLGENERATEDALWAYSASIDENTITY(STARTWITH1,INCREMENTBY1),
name
varchar(20)NOTNULL,
content
varchar(3000),
updated
timestamp,
PRIMARYKEY(id)
);

启动应用程序!

与前面讨论过的其他应用程序工件不同,这里没有关于 setup.sql 的约定。包括在此文件中的数据定义由一个启动处理程序执行 — 提供给 Project Zero 应用程序的代码,在应用程序启动之后的某个阶段执行。

首先,我们创建当 Project Zero 应用程序启动时将要执行的 Groovy 脚本。如果数据库不存在,我们希望该脚本执行在 setup.sql 中找到的语句。我们将采用一个开发时的小技巧,但请不要在产品环境中使用。目前还不支持实时修改现有的数据库模式。因此,如果应用程序的模式改变了,必须使用启动处理程序将此数据库从文件系统中删除。为了演示的需要,我们首先演示最佳实践。但这个简单的实现足以说明 zero.data API 的用法(参见清单 8):


清单 8. app/scripts/startup.groovy 的内容

importzero.data.groovy.Manager

//ifderbydatabasealreadyexists,don'trunthescript
defdb_dir=newFile('db/wiki')
if(!db_dir.isAbsolute())
db_dir
=newFile(config.root[],'db/wiki')

if(!db_dir.exists())...{
defbuffer
=newStringBuffer()
newFile('setup.sql').text.split(' ').each()...{line->
line
=line.trim()
if(line&&!line.startsWith("--")&&!line.toLowerCase().startsWith("connect"))
buffer
<<line<<' '
}

defstatements
=buffer.toString().split(';')
defdata
=Manager.create('wiki')
try...{
data.inTransaction()
...{
statements.each()
...{statement->
statement
=statement.trim()
if(statement)
data.update(statement)
}

}

}
catch(Throwableerror)...{
thrownewRuntimeException("Setupdatabaseerror:${error.message}",error);
}

}

要在启动时执行这个脚本,还需要进行相关的配置。方法是在 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 是动态的并且可以检测到绝大多数更改。现在,表单已经创建了,可以开始为控制器和视图编写代码了。让我们从呈现一个页面开始。

呈现动态 HTML 

要呈现一个页面,我们需要一个 hook,Project Zero 运行时利用它将控制权转交给 wiki 应用程序。我们将以一个控制器的形式在 app/public 目录中实现这一目的。我们将调用呈现功能 “view.groovy”。首先,需要一个 Manager 执行页面查询。接下来,我们将执行一个查询来检索数据库中的某一行。如果此行存在,我们就呈现它。如果不存在,就显示一个创建页面。此实现如清单 10 所示:


清单 10. view.groovy 实现

defname=request.params.name[]
defdata
=newzero.data.Manager('wiki')
defpage
=data.queryFirst("SELECT*FROMpagesWHEREname=$name")
if(page){
request
.page.name=page.name
request
.page.content=page.content
request
.view='view.gt'
}
else{
request
.page.name=name
request
.view='create.gt'
}
render
()

我们现在需要创建 'view.gt' 与 'create.gt' 视图来支持 view.groovy 控制器。两个视图均使用 Groovy 模板在请求范围呈现数据(参见清单 11 和 12):


清单 11. view.gt 实现

<html>
<head>
<title><%=request.page.name[]%></title>
</head>
<body>
<h1><%=request.page.name[]%></h1>
<%=request.page.content[]%>
<hr/>
<ahref="<%="${getRelativeUri('/edit.groovy')}?page=${request.page.name[]}"%>">
Editthispage?
</a>
</body>
</html>

清单 12. create.gt 实现

<html>
<head>
<title><%=request.page.name[]%>-Create</title>
</head>
<body>
<h1><%=request.page.name[]%></h1>
Thispagedoesnotexist.
<ahref="<%="${getRelativeUri("/edit.groovy")}?name=${request.page.name[]}"%>">
Create?
</a>
</body>
</html>

更新数据库

我们在前面的章节中创建了两个视图:一个被呈现的 wiki 页面和一个创建 wiki 页面的表单。为了支持这些视图,还需要创建另外的控制器和视图。也就是说,view.gt 视图链接到 edit.groovy 控制器,create.gt HTTP 通过呈现 edit.gt 将其表单 POST 给 save.groovy。edit.groovy 检索数据并显示一个与创建表单类似的表单,只不过,这里的表单是用已存在的 wiki 页面内容填充的。save.groovy 向数据库表中插入一行或更新一个已存在的行。清单 13 到 15 显示了它们的实现:


清单 13. edit.groovy 实现

defname=request.params.name[]
defdata
=zero.data.groovy.Manager('wiki')
defpage
=data.queryFirst("SELECTCOUNT(*)FROMpagesWHEREname=$name")

if(page)...{
request.page.content
=page.content
}
else...{
request.page.content
=""
}

request.page.name
=name
request.view
='edit.gt'
render()

清单 14. edit.gt 实现

<html>
<head>
<title><%=request.page.name[]%>-Editing</title>
</head>
<body>
<h1>Editing<%=request.page.name[]%></h1>
<formmethod="POST"
action
="<%="${getRelativeUri('/save.groovy')?name=${request.page.name[]}"%>">
<textareaname="content"rows="20"cols="60"><%=request.page.content[]%></textarea>
<inputtype="submit"value="SavePage"/>
</form>
</body>
</html>

清单 15. save.groovy 实现

defname=request.params.name[]
defdata
=zero.data.groovy.Manager('wiki')
defpagedata.queryFirst(
"SELECT*FROMpagesWHEREname=${name}")
defcontent
=request.params.content[]

if(page)...{
data.update(
"UPDATEpagesSETcontent=${content}")
}
else...{
data.update(
"INSERTINTOpages(name,content)VALUES($page_name,$content")
}


uri
="${getAbsoluteUri('/view.groovy')}?name=${name}"
request.headers.out.Location
=uri
request.status
=HttpURLConnection.HTTP_MOVED_TEMP

测试应用程序

您现在拥有了一个基本的 wiki 应用程序。由于它非常简单,所以必须提供一个不存在的 wiki 页面引导应用程序。输入 http://localhost:8080/view.groovy?name=HomePage 可以实现此操作,从而得到图 7 所示的页面:


图 7. 缺失主页
缺失主页

系统提示页面不存在。这很好,因为它说明应用程序正在工作。我们可以给 URI 的名称参数任意赋值,比如 “HomePage” 就不错。接下来,需要为主页创建内容。输入任意 HTML。还可以在表单中添加一个链接到其他页面(即使我们知道该页面并不存在,也可以添加)的链接,如图 8 所示:


图 8. 编辑主页
编辑主页

现在,我们单击 Save Page 按钮将页面保存到数据库。然后重定向以查看我们刚刚编辑过的页面,如图 9 所示:


图 9. 找到主页
找到主页

如果链接到了一个不存在的页面(这很容易发生,因为此时数据库中只有一个页面),那么单击此链接,就会再次跳转到缺失页面屏幕。您可以为所链接的页面创建一个页面,如图 10 所示:


图 10. 缺失 CodingTips
缺失 CodingTips

以上就是有关创建简单 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
关于下载方法的信息


参考资料

学习

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics