xquery2

在Java环境中使用 XQuery
来源:ibm 发布时间:2008-07-24 收藏 投稿 字体:【大 中 小】

将 XQJ JAR 添加到类路径

现在需要修改类路径,使其包含 XQuery JAR 和存储在 lib 目录下 ddxq.jar 中的 XQJ 类。这不是 原来下载的 JAR,而是在上面解压过程中由 JAR 安装的(如果 DataDirect 采用 ZIP 或 .tar.gz 压缩文件可能就不那么容易混淆了)。可以手工设置类路径,也可使用 shell 脚本、.profile 文件或者使用 IDE 将 JAR 添加到类路径中。要保证类路径中能够访问到 ddxq.jar。

建立数据库连接

DataDirect 下载也提供了数据库连接功能,从而能够对关系数据库运行 XQuery。填写下载表单的时候,必须选择(可能)连接的数据库。然后 DataDirect 定制下载以便能够设置数据库。这超出了本文的讨论范围,但是如果除了对磁盘上的 XML 文档使用 XQuery 外,也对使用 DataDirect 库连接到数据库感兴趣,可以查看 参考资料 小节的有关链接。安装的 lib 目录中还有其他很多 JAR 文件,但是简单的文件查询用不到这些文件。如果以后使用 DataDirect 数据库连接,也可研究一下这些 JAR。

在 Java 中运行 XQuery

掌握了 XPath 和 XQuery 并且类路径中有了 XQJ 实现之后,就可以编写 Java 代码运行查询了。每个程序都包含两个基本的步骤:

建立/访问 XQuery 数据源。
执行 XQuery。
这两个步骤都很简单,而且只要不改变 XQuery 实现,在所有程序中第一步都是一样的。实际上可以将处理数据源配置和连接的代码打包成一个工具类(留给读者作为练习)。

处理 XQuery 数据源

数据源和数据库

对于 DataDirect 这类希望在静态 XML 查询之外提供数据库连接功能的供应商来说,数据源是一个关键。一旦建立了连接对象,就可以对其运行查询,供应商负责对静态 XML 文档或者各种类型的关系数据库执行查询。需要检查 DataDirect 以及数据库供应商的文档,确定您的数据库是否支持 XQuery。

如果熟悉 JDBC 或者编写过 n-层数据驱动的应用程序,您可能已经对数据源的概念非常熟悉了。在这里,数据源就是一个连接对象,如何连接以及连接到哪里都被抽象化了。因此,数据源可能是到 MySQL 数据库的网络连接,也可能是到静态 XML 文档的基于文件的连接。一旦建立了数据源,就可以操作它而不用关心连接的语义了。

如果只需要查询本地磁盘上的 XML 文档(也是本文的重点),连接的设置非常简单。清单 3 中的简单 Java 程序建立了可以查询的数据源。

清单 3. 建立查询数据源

package ibm.dw.xqj;

import com.ddtek.xquery3.XQConnection;
import com.ddtek.xquery3.XQException;
import com.ddtek.xquery3.xqj.DDXQDataSource;

public class XQueryTester {

// Filename for XML document to query
private String filename;

// Data Source for querying
private DDXQDataSource dataSource;

// Connection for querying
private XQConnection conn;

public XQueryTester(String filename) {
this.filename = filename;
}

public void init() throws XQException {
dataSource = new DDXQDataSource();
conn = dataSource.getConnection();
}

public static void main(String[] args) {
if (args.length != 1) {
System.err.println(“Usage: java ibm.dw.xqj.XQueryTester [XML filename]”);
System.exit(-1);
}

try {
String xmlFilename = args[0];
XQueryTester tester = new XQueryTester(xmlFilename);
tester.init();
} catch (Exception e) {
e.printStackTrace(System.err);
System.err.println(e.getMessage());
}
}
}

看起来很长和复杂。主要是因为该测试程序按照非常模块化的方法编写 — 使用构造函数 init() 方法 — 以便不需要很大修改就能用于您自己的程序。

该程序取得文件名(从命令行获得并传递给类的构造函数),然后执行下面的代码:

dataSource = new DDXQDataSource();
conn = dataSource.getConnection();

首先建立一个新的数据源,该对象的类型是 com.ddtek.xquery3.xqj.DDXQDataSource。由于没有连接到数据库,不需要其他配置,因此使用了空构造函数。然后使用数据源得到新的 com.ddtek.xquery3.XQConnection 对象。该对象对于建立和执行新的 XQuery 表达式非常重要,虽然现在还不能执行这些功能,但是可以接受查询字符串并执行了。

查询真正的 XML 文档

还需要一个进行实际查询的 XML 文件。下载 小节包含一个压缩的 CD 目录文件,W3C 提供这个 XML 文档就是为了作为例子。该文件有 200 多行,这里不再列出来了,对于本文中的查询足够了。

清单 4 显示了该文档的一小部分,以便您能了解其结构。

清单 4. cd_catalog.xml 的一部分




Empire Burlesque
Bob Dylan
USA
Columbia
10.90
1985


Hide your heart
Bonnie Tyler
UK
CBS Records
9.90
1988


建立 XQuery

接下来需要创建实际查询。使用 Java String 就能做到。创建一个新变量保存查询字符串,如下面的 XQueryTester 类所示(这些代码在 main() 方法中):

try {
String xmlFilename = args[0];
XQueryTester tester = new XQueryTester(xmlFilename);
tester.init();

final String sep = System.getProperty(“line.separator”);
String queryString =
” for $cd in doc($docName)/CATALOG/CD ” +
” where $cd/YEAR > 1980 ” +
” and $cd/COUNTRY = ‘USA’ ” +
” order by $cd/YEAR ” +
” return ” +
{$cd/TITLE/text()}” +
{$cd/YEAR/text()}
“;
System.out.println(tester.query(queryString));
} catch (Exception e) {
e.printStackTrace(System.err);
System.err.println(e.getMessage());
}

查询选择 1981 年以后的所有采用 US 格式的 CD,按照发行年份排列,返回包含每张 CD 标题和年份的 XML。有几个地方需要注意:

docName 变量表示要搜索的文档。代码需要使用由命令行传递给程序的文档来填充该变量。
返回的不是结果集中每个节点的值,而是一个 XML 字符串。XML 可以放入另外一个更大的 XML 文档、在线显示或者传递给 XSL 处理程序。
源文档中的 XML 元素名 — CD、TITLE、YEAR 等 — 在结果集中都不见了,改用新的 XML 元素名:cd、title 和 year。
这些查询本身都很简单,只不过从另一个方面证明了 XQuery 从 XML 选择和返回数据的灵活性。

值得一提的是,如果结果集返回 XML 字符串,应该用花括号将返回字符串中的变量包围起来:

return {$cd/TITLE/text()}” +
{$cd/YEAR/text()}

花括号告诉 XQuery 处理程序要将包围起来的数据作为需要求值和替换的变量而不是文字文本。

声明外部变量

XQuery 使用变量 $docName 表示文档。但是需要显式声明该变量并告诉查询外部程序 — 这里就是 Java 程序 — 将定义该变量。在 XQuery 中需要使用 declare 语法。declare 语法格式如下:

declare variable [variable-name] as [variable-type] external;

该例中的 [variable-name] 即 $docName。[variable-type] 应该是 XML Schema 基本数据类型。多数时候,字符串使用 xs:string,整数使用 xs:int。还有其他几种类型,但这两种最常用。

因此对于 XQueryTester 类需要修改查询:

try {
String xmlFilename = args[0];
XQueryTester tester = new XQueryTester(xmlFilename);
tester.init();

final String sep = System.getProperty(“line.separator”);
String queryString =
“declare variable $docName as xs:string external;” + sep +
” for $cd in doc($docName)/CATALOG/CD ” +
” where $cd/YEAR > 1980 ” +
” and $cd/COUNTRY = ‘USA’ ” +
” order by $cd/YEAR ” +
” return ” +
{$cd/TITLE/text()}” +
{$cd/YEAR/text()}
“;
System.out.println(tester.query(queryString));
} catch (Exception e) {
e.printStackTrace(System.err);
System.err.println(e.getMessage());
}

现在只需要编写一个执行查询的函数。

运行 XQuery

运行查询需要经过以下步骤:

从 XQConnection 创建一个 XQExpression 对象。
使用 XQExpression 对象的 bindXXX() 方法把变量绑定到查询。
执行查询,结果保存到 XQSequence 对象中。
这些步骤中只有绑定变量需要一些技巧。该例中的变量 docName 需要和传递给程序的文件名联系起来。绑定字符串变量需要使用 bindString。该方法有三个参数:

javax.xml.namespace.QName 实例(JAXP 包中的一个类)和 XQuery 中的变量名
绑定到变量的值
变量类型应该匹配的原子类型
结合起来得到的方法应该是:

public String query(String queryString) throws XQException {
XQExpression expression = conn.createExpression();
expression.bindString(new QName(“docName”), filename,
conn.createAtomicType(XQItemType.XQBASETYPE_STRING));
XQSequence results = expression.executeQuery(queryString);
return results.getSequenceAsString(new Properties());
}

第一行 XQExpression expression = conn.createExpression(); 建立了一个新的表达式对象。然后,查询的文档文件名绑定到 docName 变量:expression.bindString(new QName(“docName”), filename, conn.createAtomicType(XQItemType.XQBASETYPE_STRING));。现在不用考虑 QName 对象的机制,只需要把 XQuery 中的变量的名称(没有 $ 字符)赋给它即可。下一个参数是文件名;第三个是一个非常固定的值,要求数值所符合的类型。由于查询中变量被定义为 xs:string,因此需要 XQItemType.XQBASETYPE_STRING 类型。

虽然有些代码不够直观,大部分只要查看 API 文档(链接见 参考资料)就明白了。其他 bindXXX() 方法如 bindInt()、bindFloat() 等等,都是相同的原理。

最后执行查询并返回结果序列。可以迭代结果集,或者直接将其转化成字符串让程序处理。query() 方法就是这么做的,因而不需要调用程序了解 DataDirect XQuery API。

清单 5 显示了完整的 XQueryTester 代码。

清单 5. 完整的 XQueryTester 类

package ibm.dw.xqj;

import javax.xml.namespace.QName;
import java.util.Properties;

import com.ddtek.xquery3.XQConnection;
import com.ddtek.xquery3.XQException;
import com.ddtek.xquery3.XQExpression;
import com.ddtek.xquery3.XQItemType;
import com.ddtek.xquery3.XQSequence;
import com.ddtek.xquery3.xqj.DDXQDataSource;

public class XQueryTester {

// Filename for XML document to query
private String filename;

// Data Source for querying
private DDXQDataSource dataSource;

// Connection for querying
private XQConnection conn;

public XQueryTester(String filename) {
this.filename = filename;
}

public void init() throws XQException {
dataSource = new DDXQDataSource();
conn = dataSource.getConnection();
}

public String query(String queryString) throws XQException {
XQExpression expression = conn.createExpression();
expression.bindString(new QName(“docName”), filename,
conn.createAtomicType(XQItemType.XQBASETYPE_STRING));
XQSequence results = expression.executeQuery(queryString);
return results.getSequenceAsString(new Properties());
}

public static void main(String[] args) {
if (args.length != 1) {
System.err.println(“Usage: java ibm.dw.xqj.XQueryTester [XML filename]”);
System.exit(-1);
}

try {
String xmlFilename = args[0];
XQueryTester tester = new XQueryTester(xmlFilename);
tester.init();

final String sep = System.getProperty(“line.separator”);
String queryString =
“declare variable $docName as xs:string external;” + sep +
” for $cd in doc($docName)/CATALOG/CD ” +
” where $cd/YEAR > 1980 ” +
” and $cd/COUNTRY = ‘USA’ ” +
” order by $cd/YEAR ” +
” return ” +
{$cd/TITLE/text()}” +
{$cd/YEAR/text()}
“;
System.out.println(tester.query(queryString));
} catch (Exception e) {
e.printStackTrace(System.err);
System.err.println(e.getMessage());
}
}
}

运行 XQueryTester

编译该程序并运行:

[bdm0509:~/Documents/developerworks/java_xquery]
java ibm.dw.xqj.XQueryTester cd_catalog.xml
Greatest Hits1982<br />Empire Burlesque1985When a man loves a woman<br />1987The dock of the bay
1987
Unchain my heart1987
Big Willie style1997<br />1999 Grammy Nominees1999

注意:为了适应在线文章的格式插入了分行符。实际的结果没有断行,而是连成一行。

实验!

该程序能够处理查询和绑定到 XML 文档。现在可以尝试不同的查询字符串,体会 XQuery 和从 Java 程序中运行这些查询。尝试选择所有的唱片,将返回结果的格式改为格式化文本。看看如何返回所有的唱片,从最新的到最老的,或者价格低于 10 美元的所有唱片。有了这样的程序后,很容易就能修改查询以便进行试验,甚至改变提供给程序的 XML 文档。

结束语

不了解 XQuery 就很难讨论 XQJ,不熟悉 XPath 就很难讨论 XQuery。将这些不同的部分结合起来通常意味着仅仅处理其中的一部分 — Java 程序执行查询,或者开始查询的文档中的位置 — 非常简单。这恰恰也是在 Java 中使用 XQuery 的最佳途径:这些简单的、独立的部分结合起来能够组成强大的程序。

由于必须熟悉多种不同的技术,尝试一个方面的时候应该保持其他两个方面不变。先保持 Java 程序不变,尝试不同的查询和不同的输入文档吧。然后从一个简单的查询开始,尝试在查询中使用更多的输入变量。或者改变返回语句中的 XPath,然后再修改搜索的起点。等这些部分都熟悉了之后,就会发现您的查询更复杂,Java 代码更健壮。

下一篇:

发表评论

电子邮件地址不会被公开。 必填项已用*标注

昵称 *