为 DB2 XML 数据开发 Java 应用程序

发表于:2007-05-24来源:作者:点击数: 标签:java开发xml应用程序db2
DB2 Viper 版本现在还处于 beta 阶段,其特征在于对存储、管理和查询 XML 数据提供了新的重要支持。在本文中,将学习有关如何编写访问新 XML 数据的 Java 应用程序的基础知识。您将看到如何插入、查询、更新和删除 XML 数据,如何创建访问 XML 数据的存储过程
DB2® Viper 版本现在还处于 beta 阶段,其特征在于对存储、管理和查询 XML 数据提供了新的重要支持。在本文中,将学习有关如何编写访问新 XML 数据的 Java™ 应用程序的基础知识。您将看到如何插入、查询、更新和删除 XML 数据,如何创建访问 XML 数据的存储过程,等等。

编写访问本地存储在 DB2 Viper 中的 XML 数据的 Java 应用程序与编写访问关系数据的 Java 应用程序没有太大的不同。实际上,如果您熟悉 Java Database Connectivity (JDBC),就已经差不多可以开始编写 DB2 XML 应用程序了。

在本文中,我们将逐步介绍几个常见的编程场景,比如插入 XML 数据、查询 XML 和非 XML 数据、更新 XML 数据、删除 XML 数据和创建访问 XML 数据的存储过程。但是我们首先来复习一下开发任何类型的 DB2 数据库应用程序的几个基本指导原则。

遵循典型的编程 “最佳实践”

尽管 DB2 的本机 XML 支持是新的,但是好的数据库应用程序编程实践没有变。在进入 DB2 的 XML 技术的细节之前,应该牢记以下一般原则:

  • 只寻找您需要的:如果您只需要该信息的一个子集的话,不要检索一个表的整个内容 —— 或者许多 XML 文档的整个内容。否则只会提高处理成本和降低运行时性能
  • 避免重复数据库服务器的工作:指示 DB2 根据您的需要过滤和处理数据,而不是在应用程序中做这项工作。例如,要让 DB2 按指定顺序返回结果,您不需要自己去对数据排序。类似地,要让 DB2 确保只返回独特的结果,您不需要重复检查重复值。以数据为中心的处理最好由数据库服务器而不是应用程序来完成。
  • 使您的代码容易维护:在代码中包含注释或 Javadoc,尤其是当您的应用程序包含复杂的查询时。
  • 仔细地考虑事务的范围:默认情况下,JDBC 将每个查询看作一个独立的事务。确定这是否适合您的需要,还要考虑您为事务定义的范围(和隔离级别)将如何影响整体并发需求
  • 最小化网络环境中的流量:如果避免应用程序和 DB2 之间不必要的数据传输,将会感受到更好的运行时性能。只检索需要的数据是做到这一点的方式之一。调用数据库存储过程也是有帮助的,这取决于您的工作的性质。

配置环境

要开发或运行处理 XML 数据的 Java 应用程序,DB2 不需要任何特殊的配置。实际上,可以通过使用自己选择的集成开发环境 (IDE) 或者从命令行直接利用支持的 Java Developer Kit (JDK),来编写、测试和调试 Java 程序。但是因为 DB2 Viper 是随 Developer Workbench 一起发布的,所以本文中的例子使用的是 Developer Workbench 的开发环境。本节讨论如何配置 Developer Workbench,查看一些示例数据,并探讨一些也许您会感兴趣的数据库配置参数。

DB2 Developer Workbench

DB2 Developer Workbench 基于 Eclipse 3.1 平台,后者是一个可免费下载的开放源码项目。要用该工作台编译和运行任何 DB2 XML 应用程序,需要创建一个项目并在该项目的构建路径中包含适当的 DB2 库(包括支持 DB2 的 JDBC 3.0 兼容驱动器的库)。要配置环境,需完成以下步骤:

  1. 启动 DB2 Workbench。例如,从 Windows Start 菜单,选择 DB2 > IBM DB2 Developer Workbench V9.1 > Developer Workbench
  2. 创建一个新项目。我们最初将使用一个简单的项目。切换到 Java 透视图(Window > Open Perspective -> Java),并选择 File > New > Project。根据向导指定项目名称。对于其他项,保持默认的设置。
  3. 将 DB2 库添加到项目的构建路径。高亮显示项目,右击鼠标,并选择 Properties。选择 Java Build Path,并单击 Libraries 选项卡。添加适当的 DB2 外部 .jar 文件,比如 db2jclearcase/" target="_blank" >cc.jar、db2jcc_javax.jar 和 db2jcc_license_cu.jar。
  4. 可选地,为应用程序创建一个包。高亮显示项目,右击鼠标并选择 New > Package

有关创建项目和包的详细信息,请参考在线帮助信息。

示例数据

本文中的例子使用 "Get off to a fast start with DB2 Viper"(developerWorks,2006 年 3 月)中创建的 "clients" 表。快速回顾一下,该表定义为:


清单 1. 最大宽度的示例代码清单
            create table clients(
            id    		int primary key not null,
            name  		varchar(50),
            status 		varchar(10),
            contactinfo 	xml
            )
            

图 1 展示了一个马上将插入该表的 "contactinfo" 列的示例 XML 文件。


图 1. 将插入 "clients" 表的示例 XML 数据
图 1

数据库配置参数

本文中的例子很简单,它们处理少量的 XML 数据,所以您不需要更改默认的数据库配置参数也能让它们运行。但是默认值对一些生产环境来说可能是不够的。具体来说,日志大小、Java 堆、查询语句堆和应用程序堆的设置值可能需要增加。如果这些值设置得不适当,那么运行时性能可能会很低,或者由于日志空间不够而不能将大型 XML 文档插入 DB2 表中。

可以从 DB2 Control Center(选择 Tools > Configuration Assistant)或 DB2 命令行处理器查看和更改 DB2 数据库配置参数。有关详细信息,请参考产品手册。

连接数据库

处理 DB2 XML 数据需要建立到包含数据的数据库的连接。关于该代码没有什么特殊的 —— 与编写到任何 DB2 数据库的连接的逻辑相同。

清单 2 包含一个 Helper 类,其中具有用于建立和关闭 DB2 数据库连接的方法。


清单 2. 用于获得和释放数据库连接的 Helper 类
            public class Conn {
            // for simplicity, I've hard-coded account and URL data.
            private static String user = "user1";
            private static String pwd = "mypassword";
            private static String url = "jdbc:db2:test";
            // this method gets a database connection
            public static Connection getConn(){
            Connection conn=null;
            //  load the appropriate DB2 driver and
            //  get a connection to the “test” database
            try {
            Class.forName("com.ibm.db2.jcc.DB2Driver");
            conn = DriverManager.getConnection(url, user, pwd);
            . . .
            }
            catch (Exception e) { e.printStackTrace();	}
            return conn;
            }   // end getConn();
            // this method closes a database connection
            public static void closeConn(Connection conn){
            try {
            if(conn == null) { return; }
            conn.close();
            }
            catch (Exception e) { e.printStackTrace(); }
            finally {
            try { conn.close();  }
            catch (Exception e) { }
            }
            }  // end closeConn();
            }  // end class
            

您将在执行广泛任务(比如插入和查询 XML 数据)的应用程序中调用这些方法。

插入 XML 数据

由于最初的 XQuery 规范没有解决数据库写操作(比如插入数据),所以 DB2 依赖于熟悉的 SQL INSERT 语句来允许程序员将新的 XML 数据写入包含 XML 列的表中。DB2 最多可存储 2 GB 格式良好的 XML 文档。

通常,Java 程序员需要将包含在文件中的 XML 数据插入到 DB2 中,尽管也可以从字符串、二进制数据(包括大对象)和 SQL 子选择语句插入 XML 数据。这里介绍了如何从文件和简单的字符串插入 XML 数据。有关其他插入场景的详细信息,请参考 DB2 Viper 手册。

DB2 Viper 也允许您插入 XML 文档时针对以前注册的 XML 模式进行验证或不进行验证。本文中的例子介绍了这两种方法。

插入文件时不进行验证

清单 3 中的 insertFile() 方法演示了如何将 XML 文件中的数据插入 "clients.contactinfo" 列。该方法首先定义几个变量供以后使用。前 3 个变量对应于 "clients" 表中的 ID、name 和 status 列。第 4 个是将要插入 "contactinfo" 列的 XML 文件的名称。为简单起见,值已经硬编码在该方法中;在生产环境中,输入值将以不同的方式获得。

在建立数据库连接之后,为 INSERT 语句创建一个简单的字符串。如您所见,它看起来与任何其他 DB2 INSERT 语句一样,并对四个输入列值使用参数标志符(marker)。INSERT 语句像平常一样准备好了,它的四个参数标志符也设置好了。要为 XML 列设置标志符,需打开一个 FileInputStream,并传递 XML 文件的位置。也获得该文件的长度,并使用该信息作为 setBinaryStream() 方法的输入。最后,执行该语句,检查错误,并关闭连接。


清单 3. 从文件插入 XML 数据
            public static void insertFile(){
            try {
            // for simplicity, I've defined variables with input data
            int id = 1885;
            String name = "Amy Liu";
            String status = "Silver";
            String fn = "c:/XMLFiles/Client1885.xml";  // input file
            // get a connection
            Connection conn = Conn.getConn();
            //   define string that will insert file without validation
            String query =
            "insert into clients (id, name, status, contactinfo) values (?, ?, ? ,?)";
            // prepare the statement
            PreparedStatement insertStmt = conn.prepareStatement(query);
            insertStmt.setInt(1, id);
            insertStmt.setString(2, name);
            insertStmt.setString(3, status);
            File file = new File(fn);
            insertStmt.setBinaryStream(4, new FileInputStream(file), (int)file.length());
            // execute the statement
            if (insertStmt.executeUpdate() != 1) {
            System.out.println("No record inserted.");
            }
            . . .
            conn.close();
            }
            catch (Exception e) { . . . }
            }
            

插入文件时进行验证

插入 XML 文件时进行验证只需要很少的附加编程工作。假设您已经创建并注册 "DB2 Viper 快速入门"(developerWorks,2006 年 3 月)中讨论的 ClientInfo.xsd 文件,那么您只需要修改 清单 3 中的一行代码,以指示 DB2 插入 XML 文件时进行验证。该代码涉及到 query 字符串的定义。

清单 4 所示,修订后的 INSERT 语句在为 XML 数据指定参数标志符之前调用 XMLValidate 函数。该函数也需要您指定将用于验证的 XML 模式标识符。在这里,引用的是前面注册的一个叫做 "user1.mysample" 的模式。


清单 4. 从文件插入 XML 数据时进行验证
            String query = "INSERT INTO clients (id, name, status contactinfo) " +
            "VALUES (?, ?, ?, xmlvalidate(? according to xmlschema id user1.mysample))";
            

如果您的 XML 输入文件包含根据指定模式来说有效的数据,那么 DB2 就会插入行。否则,整个语句失败,不会插入该行的数据。

插入字符串时不进行验证

清单 5 中所示的 insertString() 方法展示了如何将分配给字符串变量的格式良好的 XML 文档插入 DB2 中。逻辑与前一例子中从文件插入数据时的逻辑没有太大的不同。不是使用您的准备语句的 setBinaryStream() 方法,而是使用 setString() 方法。本例中为了简单起见,xml 变量定义中的 XML 文档已经进行了硬编码。

注意:转义字符(反斜杠)包含在是 XML 文档一部分的引号之前(比如下面例子中的 XML 版本号)。


清单 5. 从字符串插入 XML 数据
            public static void insertString(){
            try {
            // for simplicity, I've defined variables with input data
            int id = 1885;
            String name = "Amy Liu";
            String status = "Silver";
            String xml =
            "<?xml version=\"1.0\"?>" +
            "<Client>" +
            "<Address> " +
            "<street>54 Moorpark Ave.</street>" +
            "<city>San Jose</city>" +
            "<state>CA</state>" +
            "<zip>95110</zip>" +
            "</Address>" +
            "<phone>" +
            "<work>4084630110</work>" +
            "<home>4081114444</home>" +
            "<cell>4082223333</cell>" +
            "</phone>" +
            "<fax>4087776688</fax>" +
            "<email>sailer555@yahoo.com</email>" +
            "</Client>";
            // get a connection
            Connection conn = Conn.getConn();
            //   define string that will insert file without validation
            String query =
            "insert into clients (id, name, status, contactinfo) values (?, ?, ? ,?)";
            // prepare the statement
            PreparedStatement insertStmt = conn.prepareStatement(query);
            insertStmt.setInt(1, id);
            insertStmt.setString(2, name);
            insertStmt.setString(3, status);
            insertStmt.setString(4, xml);
            // execute the statement
            if (insertStmt.executeUpdate() != 1) {
            System.out.println("No record inserted.");
            }
            . . .
            conn.close();
            }
            catch (Exception e) { . . . }
            }
            

插入字符串时进行验证

正如您所预期的,验证作为字符串提供的 XML 文档只需要很少的额外编程工作。实际上,只需要修改一行代码 —— query 变量的定义。只需要将 INSERT 语句更改为调用 XMLValidate 函数,就像在 清单 4 中所做的一样。

下面是修订后的语句:


清单 6. 从字符串插入 XML 数据时进行验证
            String query = "INSERT INTO clients (id, name, status contactinfo) " +
            "VALUES (?, ?, ?, xmlvalidate(? according to xmlschema id user1.mysample))";
            

查询 XML 数据

既然知道了如何使用 Java 程序将 XML 数据插入 DB2 中,就可以开始查询 XML 数据了。本节中要详细介绍几个例子,从一个简单的任务(比如检索整个 XML 文档)开始,再前进到比较难的任务(比如基于 XML 和关系查询谓词返回部分 XML 文档)。

尽管 DB2 同时支持 SQL 和 XQuery 作为顶层语言,但是 XQuery 不提供解析参数标志符的手段。作为实际问题来讲,这意味着不仅仅需要硬编码查询谓词的应用程序中的任何 XQueries 都必须使用 SQL/XML 函数(XMLQueryXMLExists)封装在一个 SQL 语句中。 "使用 SQL 查询 DB2 XML 数据"(developerWorks,2006 年 3 月)更详细地讨论了这些函数。在这里,您将看到如何在 Java 程序中使用它们。只是为了好玩,您将看到如何在应用程序中包含一个具有硬编码查询谓词的 XQuery。

检索完整的 XML 文档

我们的第一个基于查询的方法相当简单。它只检索一个给定客户的全部联系信息。这种自然的查询很容易用 SQL 来表达。所以,如果熟悉 JDBC,您应该容易理解该代码。

清单 7 中的 simpleQuery() 方法声明几个变量,然后使用 清单 2 中定义的 helper 方法建立一个数据库连接。query 字符串包含一个简单的 SQL 语句来选择指定客户的所有联系信息。执行该语句之后,应用程序输出已经取入字符串变量(stringDoc)中的结果。


清单 7. 利用 SQL 检索完整的 XML 文档
            import java.sql.*;
            . . .
            public static void simpleQuery() {
            PreparedStatement selectStmt = null;
            String query = null, stringDoc = null;
            ResultSet rs = null;
            int clientID = 1885;
            try{
            // get a connection
            Connection conn = Conn.getConn();
            // define, prepare, and execute the query
            // this will retrieve all XML data for a specific client
            query = "select contactinfo from clients where id = " + clientID
            selectStmt = conn.prepareStatement(query);
            rs = selectStmt.executeQuery();
            // check for results
            if (rs.next() == false) {
            System.out.println("Can't read document with id " + clientID);
            }
            // fetch XML data as a string and print the results
            else {
            stringDoc = rs.getString(1);
            System.out.println(stringDoc);
            }
            . . .
            conn.close();
            }
            catch (Exception e) { . . . }
            }
            

该程序输出一行数据,其中包含指定客户的所有 XML 联系信息。

尽管这里没有展示,但是也可以使用 XQuery 来检索一个或多个完整的 XML 文档,假设您不需要在 XQuery 中合并参数标志符的话。在本文后面,您将看到一个 Java 摘录,其中使用 XQuery 来检索 XML 数据。

检索部分 XML 文档

检索部分 XML 文档是一个常见的编程任务。本例中的 Java 代码检索状态为 "Silver" 的客户的姓名和主电子邮件地址。客户姓名和状态信息存储在 SQL VARCHAR 列中,而电子邮件地址包含在 "contactinfo" 列中的 XML 文档中。

为简短起见,我们省略了前面展示的代码,只给出新的或不同的代码行。


清单 8. 利用 SQL/XML 检索关系数据和 XML 片段
            . . .
            String status = "Silver";
            try{
            // get a database connection
            . . . .
            // define, prepare, and execute a query that includes
            // (1) a path expression that will return an XML element and
            // (2) a parameter marker for a relational column value
            String query = "SELECT name, xmlquery('$c/Client/email[1]' " +
            " passing contactinfo as \"c\") " +
            " from clients where status = ?";
            PreparedStatement selectStmt = conn.prepareStatement(query);
            selectStmt.setString(1, status);
            ResultSet rs = selectStmt.executeQuery();
            // iterate over and print the results
            while(rs.next() ){
            System.out.println("Name: " + rs.getString(1) +
            "   Email:  " + rs.getString(2));
            }
            . . .
            // release resources
            }
            catch (Exception e) { . . . }
            

该代码发出一个 SQL/XML 语句,该语句调用 XMLQuery 函数。它提供一个到该函数的路径表达式,导致 DB2 导航到目标 XML 文档的 "Client" 根元素下面的第一个 "email" 元素。(注意,路径表达式是大小写敏感的。)$c 变量和 SQL FROM 子句指出在哪里可以找到这些目标文档 —— 在 "clients" 表的 "contactinfo" 列中。SQL WHERE 子句进一步将目标 XML 文档限制为只在这样的行中找到的文档,这些行中客户的 "status" 是一个确定值(在本例中是 "Silver")。

该程序的输出类似于:


清单 9. 前一个应用程序的示例输出
            Name: Lisa Hansen   Email:
            Name: Amy Liu   Email:  <email>sailer555@yahoo.com</email>
            . . . .
            

在该示例输出中,没有返回符合条件的客户 (Lisa Hansen) 的电子邮件信息,因为该元素不存在于她的 XML "contactinfo" 文档中。

根据关系谓词和 XML 谓词进行过滤

Java 程序也可以指示 DB2 基于适合于 XML 和非 XML 数据的条件来过滤查询输出。下面这个例子构建在前一个例子的基础之上,返回居住在加利福尼亚州圣何塞的 "Silver" 客户的姓名和主电子邮件地址。这个简单的查询从 XML 和非 XML 列检索数据,还基于 XML 和非 XML 列的内容限制数据。

下面这个摘录只包含从前一个例子发生了改变的部分代码。在本例中,SELECT 语句现在调用 XMLExists 作为 WHERE 子句的一部分,以限制居住在指定城市和州(分别在 citystate 变量中定义)的客户的结果。


清单 10. 基于 XML 元素值过滤 XML 数据
            . . .
            String status = "Silver";
            String state = "CA";
            String city = "San Jose";
            . . .
            try{
            . . . .
            String query = "SELECT name, xmlquery('$c/Client/email[1]' " +
            " passing contactinfo as \"c\") " +
            " from clients where status = ?"
            " and xmlexists('$c/Client/Address[state=$state][city=$city]' " +
            " passing contactinfo as \"c\", " +
            " cast(? as char(2)) as \"state\", " +
            " cast(? as varchar(30)) as \"city\" )";
            PreparedStatement selectStmt = conn.prepareStatement(query);
            selectStmt.setString(1, status);
            selectStmt.setString(2, state);
            selectStmt.setString(3, city);
            . . .
            }
            

该查询的大部分您应该已经熟悉,所以本节只着重介绍它的最后四行。XMLExists 函数指示 DB2 确定一个给定 XML 文档是否包含其中包含特定城市和州的客户地址。PASSING 子句指定可以在哪里找到 XML 文档:在 "contactinfo" 列中。CAST 函数被调用两次,以将输入参数的值(针对城市和州)强制转换适当的数据类型。

该程序的输出类似于 清单 9 展示的输出,假设 Lisa Hansen 和 Amy 都居住在加利福尼亚州圣何塞。

使用 XQuery 作为顶层语言

尽管 DB2 完全支持 XQuery 作为顶层语言,但是最初的 XQuery 规范并不解析参数标志符。作为一个实际问题,这限制了 XQueries 在 Java 应用程序中的使用。前几节演示了如何在 SQL 中嵌入 XQueries(例如,使用 XMLQueryXMLExists 函数),以合并参数标志符。本节介绍利用纯 XQuery 可以在 Java 应用程序中做什么。

下面这个例子中包含的 XQuery 类似于 "Query DB2 XML Data with XQuery"(developerWorks,2006 年 4 月)中给出的。该 XQuery 确定哪些客户居住在加利福尼亚州圣何塞。对于每个这样的客户,它会构造一个包含 "emailList"(其中包含该客户的所有电子邮件地址)的 XML 片段。最后,它返回一个 emailLists 序列。


清单 11. 利用 XQuery 检索 XML 片段
            try{
            // get a database connection
            Connection conn = Conn.getConn();
            // define, prepare, and execute an XQuery (without SQL).
            // note that we must hard-code query predicate values.
            String query = "xquery for $y in db2-fn:xmlcolumn" +
            "('CLIENTS.CONTACTINFO')/Client " +
            "where $y/Address/city=\"San Jose\" and $y/Address/state=\"CA\"  " +
            "return <emailList> { $y/email } </emailList>";
            PreparedStatement selectStmt = conn.prepareStatement(query);
            ResultSet rs = selectStmt.executeQuery();
            // iterate over all items in the sequence and print results.
            while(rs.next() ){
            System.out.println(rs.getString(1));
            }
            // release all resources
            . . .
            // catch and handle any exceptions
            . . .
            }
            

该查询有两个方面值得注意。首先,查询字符串以关键词 "xquery" 打头。这指示 DB2 使用它的 XQuery 解析器来处理查询。每当使用 XQuery 作为最外层语言时,您都需要这样做。其次,查询以大写形式引用表和列的名称。XQuery 是一种大小写敏感的语言。因为当把该信息写入它的内部编目时,DB2 通常将表和列的名称转换成大写形式,所以 XQuery 必须匹配该信息。

该程序的示例输出如 清单 12 所示。因为为每个符合条件的客户返回一个 "emailList" 项,快速扫视一下该输出,可以看到四个客户符合条件。第一个符合条件的记录包含一个电子邮件地址。第二个不包含(也许是因为客户不提供该信息);因此,它的 emailList 为空。第三个符合条件的记录指出该客户的记录中有两个电子邮件地址。第四个记录包含客户的一个电子邮件地址。


清单 12. 前一个应用程序的示例输出
            <emailList><email>newemail@someplace.com</email></emailList>
            <emailList/>
            <emailList><email>beatlesfan36@hotmail.com</email>
            <email>lennonfan36@hotmail.com</email></emailList>
            <emailList><email>sailer555@yahoo.com</email></emailList>
            

您也许感到好奇,为什么并不是每个符合条件的客户的姓名都包含在我们的结果中。答案很简单:XQuery 处理 XML 数据,而客户姓名存储在 SQL VARCHAR 列中。所以,如果您想要输出中包含符合条件的客户的姓名及其电子邮件地址,那么应该编写同时包含 SQL 和 XQuery 的查询。

更新和删除 XML 数据

要更新和删除存储在 DB2 中的 XML 数据,可使用 SQL UPDATEDELETE 语句。这些语句可包含 SQL/XML 函数,后者用于基于存储在 XML 列中的 XML 元素值来限制目标行和列。例如,您可以删除包含居住在指定邮编地区的客户的信息的行,或者只更新居住在指定州的客户的 XML(和非 XML 数据)。

因为在 UPDATEDELETE 语句中使用 SQL/XML 函数时的语法与在 SELECT 语句中使用 SQL/XML 函数时的语法相同,所以这里不再重复给出完整的代码示例。相反,只给出简要的摘录。我们首先来看 DELETE 操作。

删除的例子

删除包含 XML 数据的行很简单。只要使用一个 SQL DELETE 语句即可,可以根据需要带有一个 WHERE 子句来限制要删除的行。例如,下面的代码删除客户 ID 为 1885 的行:


清单 13. 基于关系数据值删除数据
            . . .
            int clientID = 1885;
            String query = "delete FROM clients WHERE id = ?";
            . . .
            PreparedStatement stmt = conn.prepareStatement(query);
            stmt.setInt(1, clientID);
            if (stmt.executeUpdate() == 0) {
            System.out.println("No records deleted.");
            }
            else { System.out.println("Record(s) deleted."); }
            . . .
            

如果您想要基于 XML 元素值限制 DELETE 操作,只需在 WHERE 子句中调用适当的 SQL/XML 函数即可。清单 14 使用 XMLExists 函数来指定应该删除居住在缅因州(缩写为 "ME")的所有客户的信息:


清单 14. 基于 XML 元素值删除数据
            String state = "ME";
            String query = "delete from clients " +
            " where xmlexists('$y/Client/Address[state=$state]' " +
            " passing clients.contactinfo as \"y\", " +
            " cast(? as char(2)) as \"state\" )";
            . . .
            PreparedStatement stmt = conn.prepareStatement(query);
            stmt.setString(1, state);
            . . .
            

更新的例子

可以使用 SQL UPDATE 语句或存储过程(比如 DB2XMLFUNCTIONS.XMLUPDATE)更新 XML 列中的数据。在这两种情况下,更新 XML 列都发生在文档级别而非元素级别。但是,使用存储过程进行更新的程序员不需要向 DB2 提供完整的 XML 文档。他们只需要指定要更新的 XML 元素,DB2 会保护未更改的文档数据,并更新指定的元素。发出 UPDATE 语句的程序员需要指定完整的文档(而不只是他们想要更改的元素)。

下一篇文章将要讨论 XMLUPDATE 存储过程,并会给出一些示例代码,所以这里不对它进行讨论。相反,本节将回顾两个发出 UPDATE 语句的代码示例。您会发现自己很熟悉这两个例子的逻辑。一个例子使用 XML 文件来更新 clients 表,而另一个例子则使用包含 XML 的字符串。

清单 15 通过使用包含在文件中的 XML 数据来更新客户 ID 为 1333 的联系信息。注意,新 XML 数据针对作为更新操作一部分的已注册模式进行验证:


清单 15. 从文件更新 XML 数据
            int clientID = 1333;
            String fn = "c:/XMLFiles/Client1333.xml";  // input file
            String query = "update clients set contactinfo = " +
            "xmlvalidate(? according to xmlschema id user1.mysample) " +
            "where id = ?";
            . . .
            PreparedStatement stmt = conn.prepareStatement(query);
            stmt.setInt(2, clientID);
            File file = new File(fn);
            stmt.setBinaryStream(1, new FileInputStream(file), (int)file.length());
            . . .
            

当然,您也可以使用 XML 查询谓词来指定想要更新的客户联系记录。同样,需要使用 SQL/XML 函数来指定。假设一个客户想要您更新她的传真号码,却不记得自己的客户 ID。相反,她提供自己的家庭电话号码来帮助找到她的信息。下面这个代码摘录使用 XMLExists 来限制只更新包含她的家庭电话号码的记录。注意,客户的完整联系信息被作为一个包含修订后的 XML 文档的 Java 字符串提供。


清单 16. 利用字符串更新 XML 数据
            String homeph = "4081114444";
            String xml =
            "<?xml version=\"1.0\"?>" +
            "<Client>" +
            "<Address> " +
            "<street>54 Moorpark Ave.</street>" +
            "<city>San Jose</city>" +
            "<state>CA</state>" +
            "<zip>95110</zip>" +
            "</Address>" +
            "<phone>" +
            "<work>4084630110</work>" +
            "<home>4081114444</home>" +
            "<cell>4082223333</cell>" +
            "</phone>" +
            "<fax>4087773111</fax>" +
            "<email>sailer555@yahoo.com</email>" +
            "</Client>";
            String query =  "update clients set contactinfo = ?" +
            "where xmlexists('$y/Client/phone[home=$homeph]' " +
            " passing clients.contactinfo as \"y\", " +
            " cast(? as varchar(11)) as \"homeph\" )";
            . . .
            PreparedStatement stmt = conn.prepareStatement(query);
            stmt.setString(1, xml);
            stmt.setString(2, homeph);
            . . .
            

查询构建器

如果您需要为应用程序编写查询,Developer Workbench 提供生成 SQL/XML 和 XQueries 的向导。因为大多数 Java 程序员编写需要参数标志符的应用程序,所以他们经常使用 SQL/XML。本节逐步介绍了一个简要的例子,讲述如何使用 SQL 查询构建器来生成一个类似于 清单 8 中包含的 SQL/XML 语句。

要生成 SQL/XML 语句,执行以下步骤:

  1. 准备好工作区。
  2. 指定查询的特征。
  3. 执行查询。

下面依次详细介绍这三个步骤。

准备工作区

SQL 语句是作为可从工作区的 Data 透视图访问的 “Data 项目” 的一部分创建的。要创建这样的项目,请执行以下步骤:

  1. 打开 Data 透视图。选择 Window > Open Perspective > Other > Data
  2. 创建到目标数据库的连接。在左下角的 Database Explorer 窗格中右击。选择 New Connection 并指定您的数据库名、用户名和密码。
  3. 创建一个新的 Data 项目。在左上角的 Data Project Explorer 窗格中右击。选择 New > Project > Data > Data Development Project。当出现提示时,给项目命名,并给它分配您刚才创建的数据库连接。

打开数据库连接并创建了 Data 项目之后,就可以开始构建查询了。

构建查询

为了保持本教程的简单性,创建一个 SQL/XML 语句返回具有某个状态的客户的主电子邮件地址。查询类似于下面的代码:


清单 17. 示例 SQL/XML 查询
            SELECT name, xmlquery('$c/Client/email[1]'
            passing contactinfo as "c")
            from clients where status = ?
            

执行以下步骤生成查询:

  1. 启动 SQL Builder。在 Data 项目中,高亮显示 SQL Scripts 文件夹并右击鼠标。选择 New > SQL Statement。当出现提示时,接受默认的项目名称,并为 SQL 语句指定一个名称。接受默认的语句类型 (SELECT) 并选择使用 SQL 构建器。单击 Finish
  2. 指定查询的表。在中央窗格中右击并选择 Add Table。展开 schema 文件夹并选择 "clients" 表。
  3. 指定感兴趣的列。对于本例来说,您需要在结果集中包含一个列和一个函数 (XMLQuery) 的输出。为此,需完成以下步骤:
    1. 检查显示在中央窗格的 "names" 列。
    2. 单击显示在中央窗格下面的 Column 选项卡中的第一行。单击该单元格的最右端以显示一个箭头键并选择 Build Expression。按 Enter
    3. 从显示的菜单中选择 Function
    4. 选择 XML 作为函数编目和 XMLQuery 作为函数。在 Parameter Value 1 旁边,单击 Value 单元格中的箭头并选择 Edit Expression
    5. 在 String Constant Builder 中指定适当的路径表达式 $c/Client/email[1],并单击 Finish 两次。
    6. 更改生成的 SQL 语句以在 XQuery 函数中包括一个 PASSING 子句。最后的 XQuery 函数应该读作:'$c/Client/email[1]' passing contactinfo as "c"
  4. 指定查询谓词(WHERE 子句)。对本例来说,您需要为关系列添加一个查询谓词。
    1. 在 Conditions 选项卡下的 SQL/XML 语句下,单击显示在 Column 选项卡中的第一行。单击该单元格最右端的箭头键并选择 status 列。
    2. 单击 Operator 单元格并选择等号 ("=") 操作符。
    3. 单击 Value 单元格最右端的箭头键并选择 Build Expression。按 Enter
    4. 选择 Constant,然后在出现提示时选择 String Constant
    5. 为用户输入(比如 "status")指定一个主机变量名。单击 Finish

执行查询

在构建查询之后,就可以准备运行它了。

  1. 定位到 Data 项目中的查询,右击并选择 Run SQL
  2. 当出现提示时,为客户状态(比如 "Gold" 或 "Silver")指定一个输入值,并单击 OK
  3. 在 Data Output 窗格中查看结果。

存储过程

在网络环境中,存储过程通常可减少客户机应用程序与 DB2 之间所需的通信,因此这改善了运行时性能。利用 Viper,存储过程可以包含 XML 参数和变量。

尽管详细讨论存储过程的开发超出了本文范围,但是提供了一个简单的场景,您可以看到 DB2 存储过程可以如何编写来检索部分 XML 文档。该场景使用 Developer Workbench 中的向导来生成、部署和运行必需的 SQL 存储过程代码。如果需要,您可以使用 DB2 的命令行处理器开发和部署一个等价的 SQL 存储过程。另外,还可以用 Java 编写基于 XML 的存储过程。

对于本例来说,您将编写一个存储过程来检索具有特定状态的客户的姓名和主电子邮件地址,就像前面所做的一样。尽管该过程非常简单,但是也能帮助您理解如何使用内置向导生成基于 SQL 的过程来查询并返回 XML 数据。

要创建该过程,需执行一些简单的步骤:

  1. 准备工作区。
  2. 指定过程的内容。
  3. 部署并测试过程

下面依次介绍这些步骤。

准备工作区

存储过程被定义为 Data 项目的一部分。如果已经这样做了,就打开 Data 透视图,建立一个数据库连接,并创建一个 Data 项目。有关详细信息,请参见前一节 准备工作区

创建过程

我们的基于 SQL 的存储过程调用一个简单的 SQL/XML 语句来基于调用者的输入查询 "clients" 表。该过程返回一个简单的结果集,其中包含一个 SQL VARCHAR 列(用于客户的姓名)和一个 XML 列(用于客户电子邮件)。查询将类似于下面的代码:


清单 18. 示例 SQL/XML 查询
            SELECT name, xmlquery('$c/Client/email[1]'
            passing contactinfo as "c")
            from clients where status = ?
            

构建访问 XML 数据的 SQL 存储过程的过程与构建访问非 XML 数据的 SQL 过程没什么不同。下面是做这件事的方法之一:

  1. 定义一个新的存储过程。展开新的 Data 项目,高亮显示 Stored Procedures,并右击鼠标。选择 New > Stored Procedure。根据提示验证项目名并指定一个存储过程名。保持默认的语言类型为 SQL。
  2. 指定 SQL 语句。当出现提示时,您可以直接键入查询语句或者使用向导帮助您创建一个查询语句。下面是后一种方法的步骤。
    1. 单击 Create SQL.。
    2. 接受默认的语句类型 (SELECT) 和开发过程(利用向导指导语句创建过程)。
    3. 选择 clients 表作为语句的目标。
    4. 在 Columns 选项卡下,在最终的结果集中包括两列。选择 names,然后选择 Add > Function > Next。在下一个窗口中,指定函数种类为 XML,XMLQuery 为函数签名。单击 Finish
    5. 在 Conditions 选项卡下,构造 SQL WHERE 子句。指定 clients.status 作为列,等号 ("=") 作为操作符,:input 作为值。
    6. 修改产生的 SQL 语句以包含适当的路径表达式,用于检索 "contactinfo" 列中的第一个电子邮件地址。具体来说,将 XMLQUERY 行更改为读作:xmlquery('$c/Client/email[1]' passing contactinfo as "c")
    7. 解析查询以验证没有语法错误。
  3. 指定部署信息。具体来说,您会发现 Enable Debugging 很有帮助。
  4. 可选地,审查生成的 SQL 代码。单击 Show SQL(出现类似 图 2 中的代码)。
  5. 完成存储过程。单击 Finish

图 2. 为处理 XML 数据的 SQL 存储过程产生的示例代码
图 2

部署和测试过程

创建好过程之后,现在就可以部署和测试它了。执行以下步骤:

  1. 部署过程。定位到 Data 项目中的过程,右击鼠标并选择 Deploy。接受默认设置并单击 Finish。右下角的 Data Output 窗格应该指出过程已经成功部署。
  2. 运行过程。定位到 Data 项目中的过程,右击鼠标并选择 Run。当出现提示时,为客户状态指定一个输入值(比如 "Gold" 或 "Silver")。单击 OK,并在 Data Output 窗格中查看存储过程的结果。

如果需要,您也可以在 Developer Workbench 外调用该存储过程。例如,如果已将过程命名为 "getInfo",那么您可以调用 DB2 命令行处理器,连接到数据库,并发出下面这个语句:


清单 19. 调用存储过程
            call getInfo('Silver')
            

结束语

编写处理 DB2 XML 数据的 Java 应用程序涉及到使用熟悉的 JDBC 代码来执行查询和处理查询结果。IBM 为 DB2 提供一个基于 Eclipse 的 Developer Workbench 来帮助您编写、测试和调试代码。该工作台包括很多向导,可用于浏览数据库的内容,编写访问 XML 和非 XML 数据的存储过程,编写访问 XML 数据的 XQueries,以及编写访问 XML 和非 XML 数据的 SQL/XML 语句。

致谢

感谢 Don Chamberlin、Grant Hutchison 和 Brian Payton 对本文的审阅。

原文转自:http://www.ltesting.net

评论列表(网友评论仅供网友表达个人看法,并不表明本站同意其观点或证实其描述)