在 Java 应用程序中用 pureQuery 处理 pureXML 数据

简介

在开发 Java 应用程序时,常常需要把信息保存到数据库中。这一情况影响了新的 API(比如用于 Java 数据库访问的 JDBC [Java Database Connectivity] API)、DAO(Data Access Object)等设计模式以及各种 Java 持久化技术,比如 JDO(Java Data Objects)、EJB(Enterprise Java Beans)、JPA(Java Persistence API)、Hibernate 和 EclipseLink 等等。

IBM pureQuery 这种 Java 数据访问技术提供容易使用的特性,简化了 Java 开发;同时为底层数据访问提供足够的透明性(使用 SQL),优化了应用程序的性能。关于 pureQuery 的更多信息请参见 参考资料

这些技术几乎都主要关注通过 ORM(Object-Relational Mapping)支持处理关系数据库系统。由于它们主要关注关系系统,所以非关系数据库系统(比如支持 XML 存储的数据库系统)缺少 Java 持久化技术提供的这种端到端支持。

支持 XML 存储的一种系统是 DB2 9 中的 pureXML 技术,这种原生 XML 数据管理技术提供层次化 XML 文档存储(按照 XML 文档的自然形式)、XML 查询语言、XML 索引功能和其他特性,并与 DB2 原有的关系功能相集成。

本文讲解如何扩展 pureQuery 的数据访问 API,从而支持查询 DB2 pureXML 数据库中 XML 列中存储的 XML 文档,以及把 XML 结果集映射为 Java 对象集合。通过把 pureQuery 和 pureXML 这两种新技术结合在一起,可以在 XML 数据库管理领域中利用 pureQuery 的生产力和运行时性能优势。

下面是在 Java 应用程序中使用 pureQuery 作为数据访问 API 处理 DB2 XML 数据的三种方式:

  • 在 SQL 层进行控制
    按照这种方式,使用 SQL 层在 XML 和关系格式之间执行转换,让 pureQuery 提供的现有功能可以使用 XML 数据。
  • 通过数据访问 API 进行控制
    按照这种方式,使用某种 XML 映射框架(本文使用 J2SE V6 的映射库)在 XML 文档和 Java 对象之间进行映射,并把这种功能集成在 pureQuery 的 API 定制处理函数中。
  • 在应用程序层进行控制
    实现自己的映射框架并把它集成在代表业务逻辑数据的 Java bean 中。

 

下面分别讨论按照这三种方式如何把数据库中的 pureXML 数据提取到 Java bean 中,以及如何把 Java bean 持久化为数据库中的 pureXML 数据。

 

在 SQL 层进行控制

按照这种方式,使用 SQL 层转换 XML 数据。使用 SQL/XML 语句把 XML 数据转换为关系数据,使用 XQuery 转换表达式执行 XML 子文档更新。

图 1 所示,使用 SQL 层把 XML 数据转换为关系数据,在数据库、数据访问 API 和 Java bean 之间只有关系数据流。


图 1. 在 SQL 层进行控制

 

获取数据 — 使用 XMLTABLE 以关系数据的形式公开 XML 数据

对于把 pureXML 文档中的数据装载到 Java bean 中,这种方式的开发工作量很可能是最少的,因为它使用 Data Studio Developer 中的几个 pureQuery 工具。

可以结合使用 DB2 和 pureQuery 中的特性。DB2 提供许多操作 XML 文档并以关系数据形式公开它们的功能,可以用简单的 SQL/XML 语句实现。pureQuery 提供一些基本的对象-关系映射,因此使用 SQL/XML 查询的结果集是很简单的任务。

我们来看看具体做法。先看看 清单 1 中的三个示例 XML 文档,它们分别代表一个职员:


清单 1. 示例 XML 文档

<employee id="901">
     <name>
          <first>John</first>
          <last>Doe</last>
     </name>
     <office>344</office>
     <salary currency="USD">55000</salary>
</employee>

<employee id="902">
     <name>
          <first>Peter</first>
          <last>Pan</last>
     </name>
     <office>216</office>
     <phone>905-416-5004</phone>
</employee>

<employee id="903">
     <name>
          <first>Mary</first>
          <last>Jones</last>
     </name>
     <office>415</office>
     <phone>905-403-6112</phone>
     <phone>647-504-4546</phone>
     <salary currency="USD">64000</salary>
</employee>

我们的目标是实现一个 API,它查询数据库并返回一个 Employee 对象列表,每个对象代表 employee 表中存储的 XML 文档中的一个职员。

可以使用 XML 类型的列把这三个文档存储在 DB2 中。可以使用 清单 2 所示的 DDL 创建一个名为 “employee” 的新表,其中只包含两列:INTEGER 类型的 id 列和 XML 类型的 doc 列。


清单 2. 创建 EMPLOYEE 表的 DDL 语句

				
create table employee (id int not null primary key, doc XML);

使用 清单 3 中的 SQL 语句把这三个示例文档插入 employee 表中:


清单 3. 把示例文档插入 employee 表中

				
insert into employee values(901, '<employee id="901"><name><first>John</first>
     <last>Doe</last></name><office>344</office>
     <salary currency="USD">55000</salary></employee>');
insert into employee values(902, '<employee id="902"><name><first>Peter</first>
     <last>Pan</last></name><office>216</office>
     <phone>905-416-5004</phone></employee>');
insert into employee values(903, '<employee id="903"><name><first>Mary</first>
     <last>Jones</last></name><office>415</office>
     <phone>905-403-6112</phone><phone>647-504-4546</phone>
     <salary currency="USD">64000</salary></employee>');

在把文档插入表中之后,可以使用 清单 4 所示的 XMLTABLE 函数以关系格式公开文档,使 pureQuery 可以使用这些数据:


清单 4. 从 XML 文档中提取数据的 XMLTABLE 函数

				
SELECT id, X.* FROM employee,
  XMLTABLE ('$d/employee' passing doc as "d" 
   COLUMNS 
      firstname		VARCHAR(5) 	PATH 'name/first',
      lastname		VARCHAR(5) 	PATH 'name/last',
      office		INTEGER 	PATH 'office') AS X

可以直接调用此函数,也可以根据这个语句创建视图,然后查询视图。如果需要进一步执行过滤,那么应该使用视图,或者在 SQL/XML 中使用 XMLEXISTS() 子句。

执行 清单 4 中的语句会返回以下结果:


清单 5. XMLTABLE 查询的 DB2 命令行输出

				
ID          FIRSTNAME LASTNAME OFFICE     
----------- --------- -------- -----------
        901 John      Doe              344
        902 Peter     Pan              216
        903 Mary      Jones            415

3 record(s) selected.

既然有了关系格式的数据,就该在 pureQuery 代码中使用它了。如果您是 pureQuery 新手,可以以三种方式实现对象-关系映射:自上而下、自下而上和在中间汇聚。可以通过文章 “使用全新的 IBM pureQuery 工具提高 Java 数据库开发生产力,第 1 部分: pureQuery 工具概述”(developerWorks,2007 年 9 月)了解这些方式的更多信息。

因为已经创建了 SQL/XML 语句,本文使用自下而上方式,也就是使用 SQL 生成 Employee Java bean。

首先,使用 IBM Data Studio Developer 创建 DAO (Data Access Object),EmployeeDAO。然后,在其中添加数据访问方法。我们先来实现一个用 清单 4 中的语句获取所有职员的方法。这要使用 清单 6 中的 Java 代码:


清单 6. Employee 数据访问对象中的 getEmployees 方法

				
public static List getEmployees(Data data){
     return data.queryList("SELECT id, X.* FROM employee, " +
          " XMLTABLE ('$d/employee' passing doc as /"d/" " + 
          " COLUMNS " + 
          " firstname          VARCHAR(20)     PATH 'name/first', " +
          " lastname           VARCHAR(20)     PATH 'name/last', " +
          " office             INTEGER         PATH 'office') AS X");
}
			

OK,这样就行了!工作做完了!哦,实际上还没有。还需要做几次鼠标操作以使用 pureQuery 工具,但是所有手工编程工作已经完成了。

尽管此示例是使用 pureQuery 内联风格创建的,但是也可以使用 pureQuery 注解方法风格实现相同的功能。(本文后面将把此示例转换为注解方法风格,从而在 pureQuery 中使用静态的 SQL)。可以通过以下文章了解 pureQuery 内联风格和 pureQuery 注解方法风格:

通过以自下而上方式在 Java 类中使用 SQL 语句,可以让 Data Studio Developer 生成一个结构与 SQL 结果集匹配的 Java bean。为此,只需右键单击语句文本并选择 pureQuery > Generate pureQuery code,见 图 2


图 2. 为 SQL 语句生成 pureQuery 代码

 

 

在向导的第一个屏幕中(见 图 3),选择 Generate bean for result set 选项,然后选择包和类名,取消选择 Generate annotated-method interface for SQL statement 选项:


图 3. 指定 bean 属性

 

 

单击 Next,确保取消选择所有复选框(它们提供测试类和示例应用程序等有用的功能,但是此示例只需要 bean),然后选择 Finish

这会创建一个新的 Java bean 并添加到 pdq.purexml.approach1 包中:


图 4. pureQuery 生成的 Java 文件

 

请注意 图 4 中的 pureQuery.example 包,它是由 Data Studio Developer 创建的,提供一些帮助开发人员使用 pureQuery 的实用程序方法。本文在示例主应用程序中使用其中的两个方法。

Data Studio Developer pureQuery 工具替您生成了 Java bean,现在可以开始使用它了。修改 DAO,把 getEmployees() 方法改为显式地返回一个 Employee 对象列表,而不是返回一般的 List 类型。

注意,只需要做两处修改。一处是方法签名,另一处是 pureQuery API 调用。告诉 pureQuery 应该把语句的结果集转换为 Employee 类的对象。作为额外参数传递 Employee 类,从而把这一信息传递给 API。在 清单 7 中可以看到修改的最终结果,修改之处以粗体文本显示:


清单 7. getEmployees 方法,现在返回 Employee 对象列表

 

最后,我们创建一个简单的主应用程序来测试把 XML 文档转换为 Java 对象的 DAO。

图 5 中,可以看到示例主应用程序以及执行此应用程序的控制台输出:


图 5. 示例应用程序和输出

 

 

工作完成了!只需执行几个简单的步骤,就能够把数据库表中的 XML 数据装载到业务逻辑中!

存储数据 — 使用 XQuery 转换表达式执行子文档更新

除了完全支持用 XQuery 和 XPath 语言查询 XML 数据之外,DB2 还支持用 XQuery 转换表达式更新 XML 文档。要想了解如何使用 XQuery 转换表达式更新 DB2 中的 XML 文档,请阅读文章 “在 DB2 9.5 中更新 XML”(developerWorks,2007 年 10 月)。

我们来考虑一下前一节中使用 XMLTABLE 语句的示例。在此示例中,只从现有的 XML 文档中获取四个节点值;除了提取的节点之外,XML 文档可能包含许多其他 XML 节点。因此,不建议执行完整的文档更新,因为这会用新的文档覆盖现有的文档,可能会导致信息丢失。对于这种情况,子文档更新是更新现有 XML 文档的最佳方式。

下面的示例演示如何使用 XQuery 转换表达式更新现有 XML 文档中的三个节点。

在数据访问对象类中添加一个新的 updateEmployee 方法,它更新数据库中的一个职员:


清单 8. 使用 XQuery 转换表达式执行子文档更新

 

DB2 中的 XQuery 语句支持使用 passing as 子句把参数标志传递给 XQuery。在 清单 8 中,把 firstnamelastnameoffice 的值从 Employee bean 传递给转换语句。因为使用命名的参数,所以只需把 Java bean 传递给 pureQuery API 调用,不需要像典型的 JDBC API 调用中那样逐一设置参数。pureQuery 会从 bean 中提取出参数值并把它们传递给 DB2。

注意,使用的 XQuery 转换表达式完全独立于 XMLTABLE 语句,子文档更新就在 存储在 Employee 表的 XML 列中的文档中执行。因为这在文档中直接执行,所以在更新期间不需要对 XML 文档执行任何序列化或解析。

现在,可以在示例应用程序中添加一些代码,从而把第一个职员的姓氏改为 'Rodrigues' 并把修改保存回数据库中,见 图 6


图 6. 示例应用程序,现在增加了更新功能

 

通过查看 图 6 中的输出,可以确认更新已经成功了,John 的姓氏现在是 Rodrigues。

使用 XQuery 转换表达式更新数据库中的 XML 文档的另一个优点是,可以只更新一个字段,这会减少应用程序和数据库之间的 I/O。可以在 DAO 对象中添加一个新方法,比如 updateEmployeeOffice(),它只更新职员的办公室信息,而不是更新前面获取的所有节点。如果把这个方法与判断哪些变量已经更新的逻辑相结合,就能够减少在应用程序和数据库之间传输的信息量,最终提高应用程序和数据库服务器的性能(由于减少了服务器负载)。

无论以哪种方式把数据库中的 XML 数据装载到 Java bean 中,总是可以用 XQuery 表达式在数据库中执行子文档更新。即使从数据库中获取了完整的文档,仍然可以使用子文档更新更新源文档中单独的节点值。

 

通过数据访问 API 进行控制

这种方式通过数据访问 API 控制映射。在当前的上下文中,这意味着通过 pureQuery API 实现 XML 和 Java 对象之间的映射,见 图 7


图 7. 通过数据访问 API 进行控制

 

pureQuery 没有提供嵌入式映射框架,但是它提供了插件机制,可以非常轻松地把现有的映射框架集成到常规的 pureQuery API 调用中。这些插件机制称为 RowHandlerResultHandler,它们分别支持数据行和结果集的定制处理函数。可以把定制处理函数传递给大多数 pureQuery API 方法,这使开发人员能够更灵活地定制数据处理。

获取数据 — 使用映射框架

一些 Java 框架提供 XML-Java 映射。流行的框架包括 Castor、 JAXB、 JiBX 和 XMLBeans。(关于这些框架的更多信息,参见 参考资料)。J2SE V6 也提供基于 JAXB 的 XML-Java 映射库。本文使用这些库,因为它们是 J2SE V6 系统的一部分,不需要额外设置。

可以用 J2SE V6 中的映射库定义映射,然后用这些映射将 XML 文档解组(unmarshal)为 Java 对象,并将 Java 对象编组(marshal)成 XML 文档。实现这种映射的方法是在 Java bean 中添加注解,声明 Java 类中的每个变量是否是 XML 文档中的属性或元素。

对于复杂的 XML 结构,创建这些 Java bean 会很繁琐;所以 J2SE V6 提供了一些工具,只需提供一个 XML 模式,它们就会生成所有 Java bean 以及映射过程所需的注解。

在本文提供的 可下载项目 中,除了 data 文件夹中的 XML 文件之外,还可以找到用来检验这些文档的 XML 模式。这个模式文件是 emp.xsd。

可以生成带注解的 Java bean 的工具称为 “xjc”,可以在 J2SE V6 系统的 bin 文件夹中找到它。xjc 接受的一些参数包括:

  • -d source_folder - 将在此文件夹中创建 .java 文件
  • -p package_name - 将在此包中创建 Java 文件
  • my_schema.xsd - 用于生成 bean 的 XSD 模式文件

要想为此示例生成 Java bean,可以从自己的工作空间用以下参数运行这个应用程序:

-d src -p pdq.purexml.approach2 data/emp.xsd

运行生成器的控制台输出见 图 8


图 8. 从 XSD 文件生成 Java 类

 

 

图 8 所示,在 pdq.purexml.approach2 包中生成了两个新文件:Employee.java 和 ObjectFactory.java。第一个文件是代表职员信息的 Java bean,第二个文件是一个工厂 helper 对象,用于从 XML 文档创建 Employee 对象。图 9 给出在包管理器中显示的生成的文件:


图 9. 工具生成的 Java 文件

 

查看 Employee.java 文件,就会看到一些注解。这些是由 J2SE V6 生成的用于实现 Java-XML 映射的注解:


清单 9. 工具生成的 Employee bean

 

 

注意,每个元素或属性被转换为一种 Java 类型。如果 XML 元素类型是 xs:simpleType,那么它将被转换为一种原生 Java 类型。否则,将创建一个内部类来存储此元素的内容(见 清单 9 中的 namephonesalary)。

现在已经创建了 Java bean,下一步是实现 pureQuery 代码,这些代码将从 pureXML 数据库获取 XML 数据并为获取的每个 XML 文档返回一个 Employee 对象。

实现这一目标的方法是创建一个名为 JAXBHandler 的定制 RowHandler。然后,可以把这个处理函数传递给几个 pureQuery API 调用,包括前面示例中使用的 queryList


清单 10. 定制的行处理函数 JAXBHandler

 

清单 10 中声明的 JAXBHandler 应用于 SQL 查询结果集中的每一行。对于每一行,它将执行 handle 方法。在 JAXBHandler 中,这个方法使用一个 Unmarshaller 对象把从数据库获取的 XML 文档转换为 Employee 类型的 Java 对象。如 清单 2 中的表声明所示,XML 文档存储在表的第二列中,所以可以调用 ResultSet 方法 getBinaryStream(2),从而指定希望获取结果集的第二列作为传递给 Unmarshaller 的二进制流。Unmarshaller 接收二进制流并解析它,创建相应的 Employee 对象。

在创建 JAXBHandler 行处理函数之后,只需在 pureQuery API 调用中使用它,比如像 清单 11 中这样做:


清单 11. Employee 数据访问对象的 getEmployees 方法

				
public static List<Employee> getEmployees(Data data){
     return data.queryList("select * from employee",new JAXBHandler());
}

清单 11 中的示例用以下参数调用 pureQuery 方法 queryList()

  • 一个 SQL 选择语句,它从数据库中获取几行
  • 一个 RowHandler,它用于把返回的每一行转换为 Java 对象

使用行处理函数把 XML 映射为 Java bean 的优点是,从数据库获取数据的 pureQuery API 调用与用来操作关系表的调用非常相似。只需对表执行 “全选择” SQL 查询,就可以获取表中所有职员的列表。在应用程序层,几乎不会注意到源数据是 XML 格式的,因为对 XML 数据的转换无缝地集成在数据访问 API(pureQuery)中。

通过运行一个非常简单的示例应用程序,可以看到 JAXBHandler 把 pureXML 列中的数据映射为 Employee bean 的效果。应用程序代码和输出见 图 10


图 10. 使用 JAXBHandler 执行映射的示例应用程序

存储数据 — 使用映射框架

与前一节中使用现有的 Java<->XML 映射框架把 XML 文档映射为 Java bean 相似,可以使用相同的框架进行相反的映射:从 Java bean 映射到 XML 文档。

除了把 XML 文档转换为 Java bean 的解组方法之外,J2SE V6 还提供可以用来把 Java bean 转换为 XML 文档的编组功能。

在对 XML 数据执行全文档更新时,这些功能非常方便。如果在从数据库中获取数据之后修改了 Java 对象中的大多数或所有字段,那么与执行几个单独的更新语句相比,执行全文档替换更简单。在这种情况下,使用编组器可以显著简化这个任务。

清单 12 给出 updateEmployee 方法。指定一个职员,此方法就会更新数据库中相应的记录:


清单 12. 通过使用 J2SE V6 执行编组,把 Java 对象持久化为 XML 数据

把 Java 对象转换为文本 XML 表示的步骤与把原来的 XML 文档转换为 Java 对象的步骤相似:

  1. 获得应用程序的 JAXB 上下文。
  2. 创建一个编组器。
  3. 编组 Java 对象,生成对象的 XML 表示。

把对象转换为 XML 格式之后,可以通过执行常规的 SQL 更新语句更新 EMPLOYEE 表中的记录。

很容易用一个简单的示例应用程序测试此示例中的代码,比如 图 11 所示的程序:


图 11. 使用 JAXBHandler 演示数据持久化和获取的示例应用程序

 

 

使用映射框架在 XML 和 Java 之间执行转换确实可以简化开发任务,但是应该认识到在从 DB2 获取数据时会执行额外的序列化和解析。J2SE V6 映射库解析 XML 以创建 Employee 对象。

 

在应用程序层进行控制

按照这种方式,pureXML 数据和 Java bean 之间的映射可以在应用程序层执行。


图 12. 在应用程序层进行控制

 

映射由 Java bean 实现,Java bean 用 XML 文档中的数据填充类变量并用类变量重新创建 XML 文档。

图 12 所示,XML 数据从 pureXML 数据库流入应用程序层中,在应用程序层中由 Java bean 处理 XML 数据。

获取数据 — 在 Java bean 中实现映射

在此示例中,映射将在 bean 本身中实现。仍然使用 pureQuery API 提供数据访问层,但是它把数据行 “按原样” 返回给应用程序,也就是包含两个变量(iddoc)的 Employee 对象。

通过在 Employee bean 中添加功能,可以实现自己的 XML 到 Java bean 映射。不必创建复杂的映射框架,只需在 Java bean 中添加映射功能。在真实的场景中,这种映射最好在一个实用程序库中实现,以便让应用程序的多个组件能够重用它。

可以通过创建自己的获取器和设置器方法来实现映射。为了把数据行表示为 Java bean,在从数据库中获取数据时,pureQuery 调用 bean 的设置器方法以填充 bean 变量。这些设置器 方法常常是由 pureQuery 自动生成的。按照这种方式,可以自己创建这些方法,从而实现从 XML 到 Java bean 字段的映射。

这种方式的示例 Employee bean 包含 iddoc 字段,它们存储数据库表的列;还有几个将由映射代码填充的字段。这些额外字段是 firstnamelastnameoffice(见 清单 13)。


清单 13. 用于 bean 内映射的 Employee bean

 

在 pureQuery 通过调用 setDoc() 方法填充 doc 变量时,会填充这些额外字段。如 清单 14 所示,在调用 setDoc 方法时,Employee bean 不仅把参数值赋值给 doc 变量,还会通过调用 populateVariables() 方法填充额外的类变量:


清单 14. Employee bean 的 setDoc 方法

				
public void setDoc(String doc) {
     this.doc = doc;
     populateVariables();
}

清单 15 中的代码实现 XML 文档和类变量之间的映射。本文使用 JAXP — Java API for XML Processing — 库解析 XML 文档并计算一些 XPath 表达式。(更多信息参见 参考资料)。但糟糕的是,还没有把 XML 二进制文档从 DB2 传递给 Java Document 对象的方法,所以需要由 DB2 对 XML 文档进行序列化并由 Java 库再次解析。

在决定从数据库获取数据的最佳方法时,应该记住这一点。

看一下 清单 15 中的代码,了解如何创建包含从数据库获取的 XML 文档内容的 Document 实例,以及如何应用 XPath 表达式提取用来填充 firstnamelastnameoffice 类变量的节点值。

您可能已经注意到这个 XPath 表达式与第一个示例中 XMLTABLE 视图中使用的表达式非常相似。这个步骤确实是相似的,惟一的差异是它在客户端上而不是数据库引擎中执行,因此使用不同的 API。但是,映射逻辑是相同的:提取节点值并赋值给类变量。


清单 15. 用 doc 中的内容填充其他类变量

				
private void populateVariables(){
try {
     DocumentBuilder builder=DocumentBuilderFactory.newInstance().newDocumentBuilder();
     Document document = builder.parse(new InputSource(new StringReader(this.doc)));
     XPath xpath = XPathFactory.newInstance().newXPath();
     String firstname_expression = "/employee/name/first";
     String lastname_expression = "/employee/name/last";
     String office_expression = "/employee/office";
     String fname = (String)xpath.evaluate(
               firstname_expression, document, XPathConstants.STRING);
     String lname = (String)xpath.evaluate(
               lastname_expression, document, XPathConstants.STRING);
     int office = (new Integer((String)xpath.evaluate(
               office_expression, document, XPathConstants.STRING))).intValue();
     setFirstname(fname);
     setLastname(lname);
     setOffice(office);
} catch (Exception e) {
	//handle exception
}
}

在这种方式的 EmployeeDAO 中,为了以 Employee 对象形式获取所有职员记录,只需执行一次全选择并让 pureQuery 用返回的数据创建 Employee 对象,见 清单 16


清单 16. 返回 Employee bean 的 getEmployees 方法

				
public static List<Employee> getEmployees(Data data){
     return data.queryList("select * from employee", Employee.class);
}

清单 16 中的 API 调用所示,在 pureQuery 创建和填充 Employee 对象时,执行 Employee 类的 setDoc 方法以填充 doc 变量,而且这个事件触发额外类变量的填充操作。

演示此代码的执行情况的示例应用程序与前面的示例相似,见 图 13


图 13. 使用 bean 内映射的示例应用程序

注意 图 13 中的 “控制台” 窗口输出,以及 bean 内映射代码如何正确地填充姓名和办公室字段。

存储数据 — 在 Java bean 中实现映射

在把 Java 对象持久化为使用 DB2 pureXML 存储的 XML 文档时,也可以使用同样的 bean 内映射方式。在前一个示例中创建并使用的是 set 方法,而现在需要创建自己的 get 方法,它将用 Java bean 的内容生成 XML 文档。然后,可以把生成的 XML 文档提供给 pureQuery API 更新语句,见 清单 19

为了简化此示例,我编写了一些创建 Employee bean 的文本 XML 表示的基本代码。清单 17 给出用于把 Java bean 转换为 XML 文档的代码段。


清单 17. 简化的 Employee bean 到 XML 文档映射

 

每当从 Employee bean 调用 getDoc 方法时,执行 清单 17 中的 recreateXMLDocument 方法,见 清单 18 中的 getDoc 方法体:


清单 18. 在调用 getDoc 时触发最新 XML 文档的生成

				
public String getDoc() {
     //reconstruct doc from contents of other class variables
     recreateXMLDocument();
     return doc;
}

 

getDoc 实际返回 doc 变量的内容之前,执行 recreateXMLDocument,用根据其他类变量值构建的新 XML 文档填充 doc


清单 19. bean 内映射的 Employee 数据访问对象的更新方法和语句

				
public static void updateEmployee(Data data, Employee emp) {
     data.update("update employee set doc = :doc where id = :id", emp);
}

 

在 pureQuery 为 清单 19 中的更新语句设置参数值时,为了传递 :doc 参数的值,它会调用 清单 18 中的 getDoc 方法,从而用作为参数传递给 updateEmployee() 方法的 emp 变量的 XML 表示更新数据库表 Employee 中的 doc 列。

我们再次修改示例应用程序,以便同时演示 XML 数据的持久化和获取。


图 14. 修改后的 bean 内映射示例应用程序

 

通过查看 图 14 中的 “控制台” 窗口,可以确认映射代码工作正常,因为对职员姓氏的修改已经正确地持久化到了数据库中。

 

性能至上

每个应用程序开发人员的最终目标都是创建性能尽可能好的应用程序。由于本文涉及两种不同的技术,pureXML 和 pureQuery,本节提供关于这两种技术的一些性能提示。

用 pureXML 提高数据库性能

您可能已经知道,在数据库方面,索引是获得良好性能的关键。您可能还不知道的是,DB2 pureXML 提供一种高级的 XML 索引机制,可以用它提高 XQuery 和 SQL/XML 型查询的速度。XML 索引存储在与关系索引相同的索引页面中,因此提供与关系索引相似的访问时间。在研究一些可以(而且应该)在本文开发的应用程序中使用的索引示例之前,请参考两篇关于 pureXML 性能和索引的文章:

假设希望选择 id 高于 250 且姓氏为 "Doe" 的所有职员。因为职员 id 存储在 id 列中,所以很容易通过在 SQL 语句的 where 子句中添加范围比较来实现这个筛选器。但是,职员的姓氏存储在 doc 列中存储的 XML 文档中的一个元素节点中。由于有了 DB2 pureXML 技术,对职员的姓氏进行筛选并不困难。DB2 提供 XMLEXISTS() 函数(它是 SQL/XML 标准的一部分),可以用它筛选 XML 列。更好的是,在使用 XMLEXISTS() 时可以利用 XML 列上的索引。假设希望在满足条件时对表执行全选择,SQL 语句应该像下面这样:


清单 20. 可以同时受益于关系和 XML 列上的索引的 SQL 语句

				
SELECT * FROM EMPLOYEE WHERE ID > 250 AND
     XMLEXISTS('$DOC/employee[name/last = "Doe"]')

因为 id 列是表的主列,在它上面已经创建了关系索引。为了进一步提高查询性能,还可以在 XML 列上创建索引,这会大大加快对职员姓氏的搜索速度。可以使用常规的 SQL create index 语句创建 XML 索引,但是要添加一些与 XML 相关的子句,见 清单 21


清单 21. 在 XML 文档的姓氏节点值上创建索引的 CREATE INDEX 语句

				
CREATE INDEX FNAME_IDX ON DB2ADMIN.EMPLOYEE(DOC)
     GENERATE KEY USING XMLPATTERN '/employee/name/last'
     AS SQL VARCHAR(25)

对于本文描述的把数据库数据提取到业务逻辑中的所有方式,都可以使用刚才创建的索引。只需创建索引并在 SQL select 语句中添加 where 子句,就会使用索引。

关于提高 pureXML 存储性能的更多提示,请参考文章 “DB2 9 中 15 个 pureXML 性能最佳实践”(developerWorks,2008 年 1 月)。

用 pureQuery 提高 Java 应用程序的性能

前面提供了关于提高数据库中 pureXML 数据的访问性能的一些提示。现在讨论应用程序性能。

pureQuery 的主要目标就是改进 Java 应用程序的数据访问机制。pureQuery 提供了许多重要的特性,例如通过使用静态 SQL 提供出色的可预测的性能、问题判断、优化现有的 Java 数据访问应用程序、开发工具等。

您在前面已经看到了 pureQuery 工具和 API 的效果,体会到了它们对开发人员生产力的显著促进作用。

本节主要讨论以静态 SQL 的形式部署和运行示例应用程序,从而提高 Java 应用程序的数据访问性能。pureQuery 允许在运行时在动态和静态 SQL 之间进行切换,所以开发人员可以利用动态 SQL 进行快速轻松的开发,然后切换到静态 SQL,从而利用静态 SQL 提供的出色且可预测的性能。

如果您不熟悉 DB2 中的静态 SQL,应该阅读下面两篇文章:

在使用 pureQuery 的应用程序中启用静态 SQL 有两种方法:

  • 使用 Java 注解或 XML 映射文件(它使用与 JPA XML 映射文件相同的格式)在带 pureQuery 注解的接口中定义数据访问层。
  • 使用 pureQuery 客户机优化功能。通过使用客户机优化,可以让现有的 JDBC 动态应用程序通过 pureQuery 使用静态 SQL,而不需要修改应用程序代码。

我们来使用带 pureQuery 注解的接口把现有的数据访问绑定为数据库中的静态 SQL。首先需要对方式 1 的 EmployeeDAO 进行转换,但是也可以为另两种方式创建相似的带注解的接口。

如果对使用 pureQuery 客户机优化特性感兴趣,可以阅读教程 “使用 pureQuery 优化现有的 JDBC 应用程序”(developerWorks,2008 年 8 月)。

pureQuery API 引入了一些注解,可以在 Java 接口中与方法声明一起使用它们,从而把 SQL 语句与方法关联起来。这使 pureQuery 工具能够把这个数据访问层绑定为数据库中的静态 SQL 并生成此接口的实现,这种实现在运行时能够作为静态或动态 SQL 运行。这些注解是 @Call@Select@Update,它们的名称反映执行的 SQL 语句类型。

把现有的方式 1 EmployeeDAO 转换为带 pureQuery 注解的方法接口非常简单。只需复制 EmployeeDAO 类中的方法签名,把 SQL 语句从方法内转移到 Java 方法声明前面的 pureQuery 注解体中:


清单 22. 把 EmployeeDAO 转换为带注解的方法接口

 

清单 22 所示,方法体已经被删除了,SQL 语句被转移到 @Select@Update 注解的 sql 参数中。Data Studio Developer 提供的 pureQuery 工具可以在每次构建 Java 项目时检查是否有带注解的方法接口。如果找到这种接口,就运行 pureQuery 代码生成器,为每个接口生成一个实现类,见 图 15


图 15. 带注解的接口和自动生成的实现类

自动生成的实现类实现接口中定义的数据访问。它创建对声明的 SQL 语句的调用,实现 SQL 结果集和方法返回类型之间的映射,或者实现方法参数和 SQL 语句参数标志之间的映射(如果有参数标志的话)。

为了把这个数据访问层绑定为数据库中的静态 SQL,在 Package Explorer 中右键单击 EmployeeData.java 并选择 pureQuery > Bind...,见 图 16


图 16. 绑定带注解的方法接口

在执行绑定 过程时,在 Data Studio Developer 的控制台窗口中显示一条消息,指出对接口的绑定是否成功,见 图 17


图 17. 绑定操作的结果

如果一个项目包含多个带注解的方法接口,可以每次绑定一个,也可以同时绑定它们 — 选择项目而不是一个接口,右键单击并选择 pureQuery > Bind pureQuery Application

为了以静态模式而不是动态模式运行应用程序,必须通过设置一个运行时标志来通知 pureQuery。这个标志名为 "pdq.executionMode",可以在请求 EmployeeData 接口的实现时把它传递给 SampleUtil 类。


图 18. 使用静态 SQL 执行示例应用程序

图 18 中的代码把运行时属性 pdq.executionMode 设置为 STATIC 并把此属性传递给 SampleUtil.getData 方法,这个方法将为 清单 22 中声明的方法接口创建一个实例类。应用程序的其余部分与前面的示例相同,但是在运行这个应用程序时,所有数据库调用都执行为 SQL 语句预编译的访问路径。

除了在 pureQuery 中使用静态 SQL 之外,您还应该了解几个 pureQuery 最佳实践。在开始开发自己的 pureQuery 应用程序之前,应该阅读文章 “编写高性能 Java 数据访问应用程序,第 3 部分:Data Studio pureQuery API 最佳实践”(developerWorks,2008 年 8 月)。这些最佳实践非常容易使用,能够显著改进应用程序的性能和内存使用量。

结束语

本文讲解了在同一解决方案中结合使用 pureQuery 和 pureXML 的三种方式。可能还有实现这一目标的其他方法,但是本文讨论的这些方式是最常用的。

究竟选用哪种方式取决于几个因素。下面的表格列出每种方式的优点和缺点,有助于您做出选择:


表 1. 从数据库获取数据

方式优点缺点
在 SQL 层进行控制(使用 XMLTABLE 函数)
  • 它利用实现了对象-表映射的现有 pureQuery 映射功能,可以减少开发工作量。
  • 只获取需要的字段,可以减少 I/O。
  • 随着需要的字段增加,SQL/XML 语句会变得太长太复杂。
  • 在需求发生变化时,必须同时更新 SQL/XML 语句和 bean。
通过数据访问 API 进行控制(使用 J2SE V6 映射库)
  • 应用程序和 SQL 层不必理会映射。在需求发生变化时,不会影响这些层。
  • 它提供的工具可以生成新的 bean 和映射代码,从而帮助应对需求的变化。
  • 与其他两种方式相比,开发人员的负担更轻。SQL 语句更简单,映射由框架执行。
  • 对于大型 XSD 文件,Java 对象层次结“构会变得过分复杂。
  • 获取完整的文档,会增加 I/O。这会导致运行时性能问题。
在应用程序层进行控制(使用定制的 bean 内映射)
  • 如果需要获取更多或更少的字段,只需修改 bean 内映射。数据访问 API 和 SQL 层不必理会这些变化。
  • 很容易为同一 SQL 语句开发具有不同内部映射的多个 bean,可以通过创建 bean 满足一种需求或一组需求。
  • 实现映射需要额外的工作。
  • 获取完整的文档,会增加 I/O。这会导致运行时性能问题。



表 2. 把数据持久化到数据库中
方式优点缺点
在 SQL 层进行控制(使用 XQuery 转换表达式)
  • 只更新需要的字段,可以减少 I/O。
  • 可以在一个语句中更新几个节点值。
  • 更新大量节点需要复杂的 XQuery 语句。
  • 按次序指定要更新的节点可能涉及复杂的逻辑。
通过数据访问 API 进行控制(使用 J2SE V6 映射库)
  • 自动执行 XML 编组。
  • 通过运行工具生成新的 bean 和映射代码,从而应对需求的变化。
  • 全文档更新,会增加 I/O。
在应用程序层进行控制(使用定制的 bean 内映射)
  • 数据访问层不必理会映射。
  • 可以通过添加逻辑,避免在没有修改时重新创建文档。
  • 实现序列化需要开发人员做额外的工作。
  • 如果没有从原来的 XML 中提取所有字段,就需要仔细设计映射,以保证在更新时不会丢失未从数据库中提取的数据。
  • 全文档更新,会增加 I/O。

尽管 DB2 pureXML 进入市场只有几年时间,但是它已经成为半结构化数据管理领域的领先技术,许多客户已经使用这种技术改进了他们的重要系统。关于 DB2 pureXML 成功应用案例的更多信息,请访问 DB2 pureXML 网站(见 参考资料)。

pureQuery 的诞生还不到一年,但已经证明了它在优化对分布式和大型机 DB2 服务器的 Java 数据访问方面的价值,这主要是由于它无缝地支持静态 SQL 并实施 JDBC 最佳实践。

本文的目标是介绍结合使用这两种技术的方式,帮助您在自己的环境中使用它们,从而进一步提高解决方案的性能、安全性、可管理性和问题诊断,获取更大的投资回报。

我们的客户经常提出的两个问题是:如何结合使用 pureXML 和 pureQuery?如何通过 pureQuery 使用 pureXML?本文尝试回答这些问题,解释这两种技术之间的差异以及如何把它们集成在一起。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值