Wolfgang Meier 的开放源码的 eXist 数据库可能是当今最流行的原生 XML 数据库(但这并不是说它是最好的)。eXist 是用 Java™ 编程语言编写的,可运行在大多数主要平台上。程序通过 eXist 绑定的 HTTP 服务器与 eXist 交互。SOAP、XML-RPC 和 RESTful 接口它都提供了,您可以通过这些接口向核心服务器提交 XPath、XQuery 和 XUpdate 请求。命令行和 GUI 客户机也是可用的。
安装 eXist
eXist 需要 Java 1.4 或更高版本,否则,所有必需的依赖关系都将被绑定。事实上,对于服务器端开放源码项目来说,安装 eXist 已经是相当容易了。其他很多项目,不管是开放源码的,还是非开放源码的,安装时都可以从安装 eXist 受到启发。安装程序是用 IzPack 构建的。发行版是一个 JAR 档案文件。要安装 eXist,只要像下面这样运行该档案文件即可:
$ java -jar eXist-1.0b2-build-1107.jar
|
安装程序打开一个 GUI,询问您要将 eXist 目录安装在哪里。我把它安装在 /home/elharo/eXist 中。eXist/bin 目录包含必需的启动脚本。要启动服务器,可执行 startup.sh (UNIX®) 或 startup.bat (Microsoft® Windows®):
该命令在端口 8080 上运行服务器,并开始服务 /eXist 中的文件。您可以从任何 Web 浏览器连接到 eXist。例如,我将 eXist 安装在 eliza.elharo.com 上,所以我可以在以下 URL 处连接到 eXist:
http://eliza.elharo.com:8080/exist/
|
(您不要在自己家里这么去尝试,因为我的防火墙会挡住您。您必须连接到您自己的服务器。)
最初,您将看到 eXist 文档,还有一些您将要去探明的示例。
将数据装载到 eXist 中
eXist 不是真正的 Web 服务器,它只是使用一个 Web 服务器作为到底层数据库服务器的方便的接口。软件包中还包含独立的 GUI 客户机和编程 API,您可以使用这些来执行各种操作。您甚至可以使用 WebDAV 从 Microsoft Windows Explorer 浏览 eXist。对于初次体验来说,可能使用简单的 GUI 客户机是最容易的。要启动客户机,可从 eXist/bin 目录执行 client.sh (UNIX) 或 client.bat (Windows):
从图 1 可以看到,默认情况下,客户机试图连接到运行在端口 8080 上的本地主机上的 eXist 数据库。您可以在 URL text 字段指定另外的主机和端口。这一个窗口也会要求输入用户名和密码。默认情况下,用户名是 admin,可以将 password 字段保持为空。
图 1. 连接到 eXist
|
是的,您可以管理用户、设置密码、设置特权、将用户分配到具有不同访问权限的组,还可以完成完全生产环境中所有其他必需的任务。然而限于篇幅,许多这样的选项都没法一一介绍,而要将重点放在数据库上。
|
|
您登录之后,客户机显示图 2 所示的 GUI。最初,eXist 带有一个集合,叫做 system,其中存储有用户信息。现在您不想使用这个集合,而是通过选择 File > New Collection 为文档创建一个新的集合。我创建了一个名为 books 的集合。要打开集合,可在 GUI 中双击它。打开一个集合后,要上载文档,可单击看起来有点像一张弯曲的纸、旁边有一个加号的图标。
图 2. eXist 管理客户机
我首先上载两个小文档,数据库毫无意见地接受它们。然后我尝试上载我的 Processing XML with Java 一书的完整文本。这个操作悄无声息地失败了,没有给出任何错误消息。不通过 GUI 客户机上载,改为通过 Web 接口上载也失败了。但是该接口给出了一个堆栈跟踪,有助于调试问题。这表明 eXist 没有解析文档类型声明中使用的相对 URL。要装载具有外部 DTD 子集的文档,您必须手动在服务器的文件系统上安装 DTD,并编辑一个编目文件,告诉数据库要装载的文档在哪里;然后,必须重启数据库服务器,使它重新装载编目文件。这是一个主要的争论点,尽管每个不同的 DTD 您通常只需要安装一次。在您的文档不使用 DTD 或者只使用少量不怎么改变的 DTD 时,eXist 工作得最好。
查询 eXist
eXist 支持 XPath 和 XQuery(关于二者的更多信息,请参见 参考资料)。eXist 使用 2003 年 11 月 XQuery 工作草案中的 XQuery 语法。现在正在努力将数据库更新到使用更多近期工作草案中的语法。对于基本的 For-Let-Where-Order-Return (FLWOR) 查询来说,草案之间的差别并不大。
要输入针对集合的查询,单击 GUI 客户机中的望远镜图标以显示图 3 所示的窗口。
图 3. eXist 查询窗口
烦人的是,复制和粘贴在该界面中不起作用,所以必须手动地键入所有查询。当然,这个程序只是用于测试和体验,不能用它来与诸如 Oracle 这样的您必须在其中键入原始 SQL 的数据库进行频繁的交互。在对想要运行的查询有了相当好的主意之后,您就可以编写程序根据算法来生成和提交查询,这一点我下面就会讨论到。
编写与 eXist 交互的程序
IBM®、Oracle 和 JSR 225 专家组的其他成员当前正在定义一个用于取代 XQuery 的 API,其中 XQuery 由 JDBC 用于取代 SQL。但是直到这个过程结束并在 eXist 中实现该 API,仍然有必要使用 eXist 的本机 API。您可以通过 SOAP、XML-RPC、WebDAV 或 HTTP 接口访问这个 API。任何支持这些协议之一的 API 都可以与 eXist 通信。例如,可以使用 JAX-RPC、通过 SOAP 与 eXist 通信,或者使用 java.net、通过 HTTP 与它通信。
RESTful HTTP 接口是最简单的,并且是这些选项中最广泛可用的。例如,假设您想要找到包含单词 “XSLT” 的 books 集合中的所有 para
元素。清单 1 中的 XQuery 找到所有这样的元素。
清单 1. 一个示例 XQuery
for $p in //para
where contains($p, "XSLT")
return $p
|
您从下面这个 URL 获得(GET
)该查询:
http://eliza.elharo.com:8080/exist/servlet/db/books/
|
其中 eliza.elharo.com
是网络主机,数据库就运行在它上面;8080
是端口;/exist/servlet/db
分别识别 Web 应用程序、servlet 和数据库;books
是您在该数据库查询的特定集合。eXist 允许嵌套的集合。例如,books 集合可能包含单独的小说和非小说集合,这些集合可在下面这些 URL 处得到:
http://eliza.elharo.com:8080/exist/servlet/db/books/fiction/
http://eliza.elharo.com:8080/exist/servlet/db/books/nonfiction/
|
然而对本文来说,您想要查询所有的书籍,包括小说和非小说。XQuery 被作为 URL 的查询字符串(URL 中引号后的部分)中的 _query
字段的值发送。它必须以通常方式用百分比编码(例如,空格成了 %20
,双引号成了 %22
,等等)。因此,您可以通过获得(GET
)下面这个 URL 将清单 1 中的查询发送到服务器:
http://eliza.elharo.com:8080/exist/servlet/db/books/?_query=
for%20$p%20in%20//para%20where%20contains($p,%20%XSLT%22)%20return%20$p
|
服务器将查询结果封装在 exist:result
元素中之后发送回来,如清单 2 所示。
清单 2. 示例查询的结果
<exist:result xmlns:exist="http://exist.sourceforge.net/NS/exist"
exist:hits="148" exist:start="1" exist:count="10">
<para><quote>HTML? You must be joking</quote> said the fourth, a computer
science professor on sabbatical from MIT, who was engrossed in an XSLT
stylesheet ...</para>
<para>XSLT and the TrAX API</para>
<para>Combine functional XSLT transforms with traditional imperative Java code</para>
<para>The TrAX API for XSLT processing</para>
<para>Once you′re comfortable with one or more of these APIs, you
can read Chapters 16 and 17 on XPath and XSLT.
However, those APIs and chapters do require some knowledge of at least one
of the three major APIs.</para>
...</exist:result>
|
其他可选查询字符串变量控制结果是否是良好打印格式的、用什么元素封装结果、返回多少匹配的值(默认情况下,eXist 只返回前 10 个匹配的值),等等。
由于这全是通过 HTTP GET
完成的,所以您可以简单地通过在 Web 浏览器中键入适当的 URL 而执行该查询。当然,任何通过 HTTP 传递信息的软件库也可以发送该查询并取回 XML 流形式的结果。若要用 Java 语言来编写该查询,您可以使用 URLEncoder
类来编码查询字符串,用 URL
来提交查询,并用 XOM 来处理结果,如清单 3 所示。
清单 3. 用 Java 代码查询 eXist
String xquery = "for $p in //para"
+ " where contains($p, "XSLT") "
+ " return $p";
String encodedQuery = URLEncoder.encode(xquery);
URL u = new URL("http://eliza.elharo.com:8080/exist/servlet/db/books/?_query=");
+ encodedQuery);
InputStream in = u.openStream();
Document doc = (new Builder()).build(in);
// work with the document...
|
像这样的 HTTP 接口是完全语言独立的。您可以容易地用 Perl、Python、C、C# 或任何其他具有一个简单 HTTP 库和一些 XML 支持的语言重新产生清单 3 中的功能。查询这样的数据库最有效的一种方式是编写一个 XSLT 样式表来格式化结果。
插入文档
XQuery 允许您从数据库取出信息。但是放入数据呢?这甚至更加容易。不是发送 GET
请求,而是发送 PUT
请求。放入(PUT
)数据到其中的 URL 就是文档将要放入数据库中的 URL;请求的主体是将要存储的文档。例如,清单 4 中的 Java 代码从 Cafe con Leche Web 站点取得 RSS 提要,并将它放入名为 20050401 的联合集合中。
清单 4. 用 Java 代码将文档插入 eXist 中
URL u = "http://www.cafeaulait.org/today.rss";
InputStream in = u.openStream();
URL u = new URL("http://eliza.elharo.com:8080/exist/servlet/db/syndication/20050401");
HttpURLConnection conn = (HttpURLConnection) u.openConnection();
conn.setDoOutput(true);
conn.setRequestMethod("PUT");
conn.setHeaderField("Content-type", "application/xml");
OutputStream out = conn.getOutputStream();
for (int c = in.read(); c != -1; c = in.read()) {
out.write(c);
}
out.flush();
out.close();
in.close();
// read the response...
|
将新文档放入数据库中(PUT
)通常需要认证。eXist 的 REST 接口支持 HTTP Basic 认证。Java 语言通过 java.net.Authenticator
类支持这种认证。本文不做详细讨论,但是简要来说,您必须用一个知道(或者知道如何询问)数据库用户名和密码的类来继承 Authenticator
,然后安装该子类的的一个实例作为系统默认的认证程序(authenticator)。
删除文档
需要从集合中删除文档吗?只要发送一个 DELETE
请求到适当的 URL 即可,如清单 5 所示。
清单 5. 删除 eXist 中的一个文档
URL u = new URL("http://eliza.elharo.com:8080/exist/servlet/db/syndication/20050401");
HttpURLConnection conn = (HttpURLConnection) u.openConnection();
conn.setRequestMethod("DELETE");
conn.connect();
// read the response...
|
同样,在实践中也需要通过一个 Authenticator
对象提供用户名和密码。
更新文档
最后也是最复杂的操作是修改数据库中的信息。例如,假设我要将 e-mail 地址从 elharo@metalab.unc.edu 更改为 elharo@macfaq.com。因此,我想要将所有的 <email>elharo@metalab.unc.edu</email>
元素更改为 <email>elharo@macfaq.com</email>
。XQuery 不提供这种能力,所以 eXist 使用 XUpdate 来代替。清单 6 中的 XUpdate 查询作出了更改。
清单 6. 使用 XUpdate 来更新 eXist 中的文档
<xupdate:update
xmlns:xupdate="http://www.xmldb.org/xupdate"
select="//email[.=′elharo@metalab.unc.edu′]">
elharo@macfaq.com
</xupdate:update>
|
由于该操作改变了一个资源,所以您需要使用 POST
方法来将它发送到服务器。您发出想要更改的文档的 URL 并在请求体中给出 XUpdate 指令。
我只介绍了 REST 接口的要点。它也包含以下用途的指令:创建和删除集合、指定查询结果如何被格式化,以及提供用户凭证。HTTP 也不只是 eXist 的接口。eXist 还具有针对 Perl、PHP 和 Java 语言的本机 API,以及一般的 WebDAV、SOAP 和 XML-RPC 接口。广泛的 API 支持是 eXist 一个特别的优点。
性能、健壮性和稳定性
eXist 并不是世界上最快的数据库。您可以容易地使用一个秒表来度量它装载一个中等大小的文档所需的时间,即使在连接到本地数据库的快速硬件上也要花很长的时间。查询速度类似于质量。在比较大的集合上进行复杂的查询让您有时间去煮一杯咖啡。为了缩短文档装载和查询时间,您可以给 eXist 更多的内存。与 eXist 一起发布的默认配置指定的设置适合于具有 256 MB 左右内存的机器。如果具有一台比较强大的服务器,您可以修改 conf.xml file 给 eXist 分配更多的内存。
为了优化数据库,您可以添加索引。默认情况下,eXist 对元素和属性节点以及文档的全部文本做索引。您可以为可能出现在您的查询中的特定节点集指定附加的范围索引。例如,如果您知道自己可能执行大量的查找 para
元素的查询,那么可以在 //para
上定义一个查询。这告诉 eXist 预先执行和存储文档中所有 para
元素的值,因为它们很可能后面会用到。
因此,eXist 主要适合于速度不是很重要的小型集合。如果您具有千兆字节大小的文档或者每小时要处理数千个事务,那么请寻求别的办法。
同样,我不能保证我的数据对 eXist 有效。我自己没有体验过数据破坏。但是其他开发人员会经常遇到并修复数据库破坏问题。从正面来说,eXist 使得备份数据库非常容易。非常重要的是,备份格式以真正的文本 XML 保存内容,而不是以专有的二进制格式保存;这意味着哪怕出现问题,您可以用文本编辑器来修复。如果您经常做归档备份,那么 eXist 的数据就不可能变得不可检索。
eXist 功能恰当,能满足一些基本的需求,并包含一些意想不到的附带特性,比如 XInclude 支持。事务、回滚、撤退以及一些类似的企业级特性都没有包括(事务列在 “即将实现” 的清单内);但是许多应用程序并不需要这类高级功能。
我对 eXist(就这一点来说,或者是任何其他基于 XQuery 的原生 XML 数据库)最关心的一点是底层标准和 API 的稳定性。本文基于 eXist 的最新 beta 版,该版本基于 2003 年 11 月的 XQuery 草案、于 2004 年 11 月发布。eXist 的这个版本现在 CVS 中,作了一些向后不兼容的更改,这些更改还没有完全文档化。将来还会作更多的更改,不论是 eXist 中,还是它所依赖的 W3C 规范都会发生更改。除非您适应那些需要您重新测试和编写代码的频繁更改,否则不要将 eXist 用于生产环境。
结束语
拥有的数据越多,越需要使用数据库系统来管理数据。如果数据是 XML,那么稳定的原生 XML 数据库无疑是适当的选择。eXist 是一种稳定的系统吗?很遗憾,答案是否定的。eXist 是一个有趣的研究项目,也许会在一两年内开发成一款有用的工具。但是就其当前状态来说,我无法推荐您使用它。文档是不完整的,并且经常会引起误解。没有错误消息(各地的程序员请注意,异常堆栈跟踪不能当作真正的错误消息,有时,eXist 甚至连堆栈跟踪都没有提供给您)。GUI 时常会违反用户界面标准。像复制和粘贴这样的基本特性被忽略了。在为本文做非常基本的测试期间,我遇到了多个 bug。
eXist 还没有最终完成,当前还处于 beta 版本。在版本 1.0 发布之前,我遇到的许多问题都有望被解决,但不可能在短期内解决。我知道现在就有一些人将 eXist 用于实际的工作,这让我有些想不通。要么就是他们非常幸运,要么就是他们仔细地编写查询和文档,避免了 eXist 的 bug。如果您有兴趣为一个有价值的开放源码项目作贡献,那么 eXist 就是一个值得您去贡献的项目。但是使得它对于程序员来说是一个试探性项目的不完全性也使得它不适合于生产系统。
参考资料
凡是有该标志的文章,都是该blog博主Caoer(草儿)原创,凡是索引、收藏
、转载请注明来处和原文作者。非常感谢。