当前位置:IT教程网教程中心其他整理XML → 教程内容

IBM Video Online for e-business

减小字体 增大字体 作者:佚名  来源:不详  发布时间:2007-1-8 17:42:00
Abdul H. Al-Azzawe
IBM 硅谷实验室
2002 年 6 月
进一步讨论 IBM Video Online for e-business 的 IBM DB2 开发人员域文章。

简介
IBM Video Online for e-business 是一个特别为录像租赁业设计的端到端的电子商务企业对消费者(B2C)解决方案。它是一个在线录像出租商店,允许用户通过 Web 按名称租借影片。

图 1. IBM Video Online 欢迎 Web 页面
IBM Video Online 欢迎 Web 页面

三层体系结构
IBM Video Online for e-business (简称 Video Online)由多个逻辑层构成:将数据显示给最终用户的界面层(也叫表示层)、强制实施业务规则的控制层(也叫业务逻辑层)以及用于抽取存储在 DB2® Universal Database™ 服务器中实际数据的数据层。

使用 XML 交换数据
XML(可扩展标记语言)正在迅速成为在应用程序之间或同一个应用程序的组件之间交换数据的最受欢迎和最灵活的机制。对于 B2C 多层应用程序,业务逻辑层从数据层检索 XML 数据,然后将 XML 数据转换成 HTML(超文本标记语言)以便显示给最终用户。对于企业对企业(B2B)多层应用程序,业务逻辑层从数据层检索 XML 数据,然后将该数据(仅此状态,或者根据预先定义的数据转换规则进行了修改)转发给服务消费者应用程序以供进一步处理。

出站数据片段
如 图 2中所示,Video Online 应用程序按下列方式将 XML 用作其三层间的数据交换协议:

  • 数据层以 XML 文档形式从 DB2 抽取数据:
    • SQL 存储过程返回一个或多个结果集。
    • XML 存储过程使用 XML 用户定义函数(UDF)将结果集转换成 XML。
    • XML 存储过程将 XML 数据片段返回给数据层。
  • 控制层使用 XSL(可扩展样式表语言)将 XML 文档转换成 HTML 片段。
  • 界面层合并多个 HTML 片段以生成显示给最终用户的完整 Web 页面。

图 2. XML 是同 HTML 有关的数据交换协议
XML 是同 HTML 有关的数据交换协议

DB2 支持多种为 XML 抽取数据的方法,包括使用 DB2 XML 扩展器(extender)和用户定义的函数。本文将集中讨论使用特殊目的的 XML UDF 来将存储过程结果集转换成 XML 文档。

案例研究:成员兴趣清单
为了正确地说明 Video Online 应用程序层中 XML 的使用,我们把精力集中在一个 Video Online 数据片段上 — 成员的影片兴趣清单。

在线成员兴趣清单表示成员有兴趣在以后某一时间租借但是还没有提交租借的电影名称清单。

请注意:本文中的步骤假定您已经下载了 xmloutbound.zip中的所有文件。在继续以前,请阅读 支持文件中的信息。

下面是构建 Video Online 功能以显示成员兴趣清单的基本步骤。

  • 第一步:使用 SQL 存储过程从 DB2 检索 SQL 结果集
  • 第二步:将 SQL 结果集格式化成 XML 数据片段
  • 第三步:使用 XSL 转换将 XML 转换成 HTML 片段
  • 第四步:从 HTML 片段构建 Web 页面
p>

第一步:从 DB2 检索 SQL 结果集
这一步的目的是开发从 Video Online 数据库查询成员兴趣清单的 SQL 存储过程。我们将描述:

  • 被检索的数据类型;
  • 影响到的表和所需的 SQL SELECT 语句;
  • SQL 存储过程源代码;
  • SQL 存储过程返回的 Video Online 结果集采样。

检索感兴趣的影片清单
Video Online 成员的兴趣清单存储在下列表中:

VODD.WishedTitles
该表含有所有成员的感兴趣的影片清单。

VODD.Title
该表含有每部影片的详细描述。

VODD.Person
该表含有对每部电影演职人员的详细描述。

VODD.TitlePerson
该表含有同每部影片片名相关联的演职人员清单。

图 3中的模式图说明了这些表之间的关系:

图 3. 感兴趣片名清单模式
感兴趣片名清单模式

要创建这些表并将所需的样本数据装入这些表,请执行下列命令(参阅 支持文件以获取更多信息):

            C:\> DB2 -tvf CreateTables.db2            
C:\> DB2 -tvf InsertData.db2

存储过程 GetMyWishedFull
图 4演示了 GetMyWishedFull 存储过程的 SQL 源代码。可以按如下方式创建该存储过程:

            C:\> DB2 -td@ -vf GetMyWishedFull.db2            

图 4. GetMyWishedFull 存储过程(包括在 xmloutbound.zip中)

            CREATE PROCEDURE VODD.GetMyWishedFull (            
      IN iMemberID INT )
    RESULT SETS 1
    LANGUAGE SQL
P1: BEGIN
    -- Declare cursor for full wished list for specified MemberID
    DECLARE cursor1 CURSOR WITH RETURN TO CALLER FOR
        SELECT
            VODD.TITLE.TITLEID AS TITLEID,
            VODD.TITLE.NAME AS TITLENAME,
            VODD.TITLE.IMAGEFILE AS IMAGEFILE,
            VODD.TITLE.AVGCUSTOMERRATING AS AVGCUSTOMERRATING,
            VODD.TITLE.AVGCRITICRATING AS AVGCRITICRATING,
            VODD.TITLE.RANK AS MOVIERANK,
            VODD.WISHEDTITLES.DATEADDED AS DATEADDED,
            VODD.TITLEPERSON.PERSONID AS PERSONID,
            VODD.PERSON.FIRSTNAME AS PERSONFIRSTNAME,
            VODD.PERSON.LASTNAME AS PERSONLASTNAME
        FROM
            VODD.WISHEDTITLES,
            VODD.TITLE,
            VODD.PERSON,
            VODD.TITLEPERSON
        WHERE
        (   VODD.WISHEDTITLES.CUSTOMERID = iMemberID
        AND VODD.TITLE.TITLEID = VODD.WISHEDTITLES.TITLEID
        AND VODD.TITLEPERSON.TITLEID = VODD.TITLE.TITLEID
        AND VODD.PERSON.PERSONID = VODD.TITLEPERSON.PERSONID )
        ORDER BY VODD.WISHEDTITLES.DATEADDED DESC, VODD.WISHEDTITLES.TITLEID;
    -- Cursor left open for caller
    OPEN cursor1;
END P1

将结果集返回给调用者
使用 WITH RETURN TO CALLER 游标类型允许包括调用存储过程在内的调用者访问游标数据。如果没有指定 TO CALLER,那么游标将只可由客户机应用程序访问。更多关于从嵌套存储过程访问结果集的信息,请参阅 使用结果集定位器获取数据。

样本 SQL 数据输出
图 5显示了对标识为 1 的成员执行 GetMyWishedFull 存储过程的结果。可以按如下方式完成这一任务:

            C:\> DB2 CALL VODD.GetMyWishedFull(1)            

图 5. 对标识为 1 的成员执行 GetMyWishedFull 存储过程的结果

TitleID TitleName ImageFile AvgCustR AvgCriticR MovieRank DateAdded PersonID FirstName LastName
2 Letha Weapon 4 t2.jpg 4.0 3.5 189 01/06/2002 1 Mel Gibson
2 Letha Weapon 4 t2.jpg 4.0 3.5 189 01/06/2002 11 Shane Black
2 Letha Weapon 4 t2.jpg 4.0 3.5 189 01/06/2002 12 Joel Silver
2 Letha Weapon 4 t2.jpg 4.0 3.5 189 01/06/2002 19 Richard Donner
3 Father's Day t3.jpg 3.8 3.2 200 01/03/2002 1 Mel Gibson
3 Father's Day t3.jpg 3.8 3.2 200 01/03/2002 13 Ivan Reitman
3 Father's Day t3.jpg 3.8 3.2 200 01/03/2002 14 Francis Veber
5 Mavrick t5.jpg 3.7 3.9 153 01/02/2002 1 Mel Gibson
5 Mavrick t5.jpg 3.7 3.9 153 01/02/2002 19 Richard Donner

请注意,片名信息重复出现了多次,因为对于每个与之关联的演职人员都会出现该片名信息。

p>

第二步:将 SQL 结果集格式化成 XML 数据片段
这一步的目的是开发将 SQL 数据转换成格式良好的 XML 文档的框架。它描述了:

  • 生成 XML 文档的定制标量 UDF;
  • 将来自 SQL 存储过程的 SQL 数据转换成 XML 文档的实际 XML 存储过程源代码;
  • 对 XML 存储过程返回的 Video Online XML 文档进行采样。

开始之前:修改 APPLHEAPSZ 以避免“内存用完”错误
将数据库应用程序堆(APPLHEAPSZ)配置参数从缺省值 128 个 4KB 的页修改为 1024 个 4KB 的页。这是因为 XML UDF 需要额外的内存来处理可能的大 XML 文档。

可以使用下列 DB2 命令来修改 APPLHEAPSZ:

            DB2 update db cfg for            sample using APPLHEAPSZ 1024            

构建 XML 文档的 DB2 XML 用户定义函数
在 DB2 中,您可以编码定制标量 UDF 来操作数据。这一节描述了一组可以用于以快捷和有效的方式构建 XML 文档的标量 UDF。可以在多种应用程序中使用这些标量 XML UDF,包括(但不限于)将结果集格式化成 XML 文档。

XML 标量 UDF 包含在 xmloutbound.zip中。更多详细信息,请参阅 支持文件。要创建这些 UDF,请输入下列命令:

            C:\> copy db2xudfs.dll %DB2PATH%\function            
C:\> DB2 -td@ -vf db2xudfs.db2

这一节含有对下列 XML 标量 UDF 的详细描述:

xdocument
创建一个内部 XML 文档。

xparameter
在生成的 XML 文档中包括参数 XML 节点。

xtable
在生成的 XML 文档中创建一个表 XML 节点。

xrow
创建一个表行和行标识符。

xtree
表示将考虑对该新行列进行聚合。

xnode
向 XML 文档添加一个新节点。

xgetdocumentsize
返回生成的 XML 文档的大小。

xgetdocument
返回该 XML 文档。

xreleasedocument
释放同该 XML 文档相关联的资源。

XUDFS.XDocument( name, maxColumns, minSize, growSize)
xdocument 标量创建一个包含所需的额外 XML 节点的内部 XML 文档。该 UDF 将整数句柄返回给新创建的 XML 文档。必须将该句柄用作剩余的 XML UDF 的输入参数。该 UDF 使用下列输入参数:

name
表示 XML 文档名称的 VARCHAR 值。该值用作根节点。

maxColumns
这一可选整数值让您指定将包含在这一文档中的每个表行所含的最大列数。该值可以用于内部 UDF 内存管理优化。在不能确定时,请传入缺省值零。

minSize
这一可选整数值让您以字节为单位指定初始文档大小。该值可以用于内部 UDF 内存管理优化,这样,指定最初的期望大小将避免今后必须增加内存,或者分配比存储结果文档所需的更多的内存。不能确定时,请传入缺省值零,这样让 UDF 实现来决定初始大小。

growSize
这一可选整数值让您指定以字节为单位的增量文档的大小。当超出了当前的文档大小时,文档会按照这里指定的值增加。类似于 minSize 参数,这一值可以用于内部 UDF 内存管理优化。不能确定时,请传入缺省值零,让 UDF 实现计算文档增长大小的最优值。

XUDFS.XParameter(xdoc, parameterName, parameterValue, options)
xparameter 标量 UDF 用于在生成的 XML 文档中包含参数 XML 节点。作为输入参数传入的文档句柄 xdoc 返回给调用者。该 UDF 使用下列输入参数:

xdoc
表示要被更新的 XML 文档句柄的整数。

parameterName
表示参数文本名称的 VARCHAR。它必须是一个有效的 XML 节点名称。

parameterValue
表示参数的文本值的 VARCHAR(或 CLOB)。

options
表示所采用的值格式化类型的整数。如果要去除参数值的结尾空白,则请指定值 1;否则请指定值 0。

调用这一 UDF 之后将下列 XML 插入到 XML 文档:

            <parameterName>parameterValue</parameterName>            

XUDFS.XTable(xdoc, tableName)
该 xtable 标量 UDF 在生成的 XML 文档中创建一个表 XML 节点。作为输入参数传入的文档句柄 xdoc 被返回给调用者。该 UDF 使用下列输入参数:

xdoc
表示要被更新的 XML 文档句柄的整数。

tableName
表示表的文本名称的 VARCHAR。它必须是一个有效的 XML 节点名称。

调用这一 UDF 之后会将下列 XML 插入到 XML 文档:

            <tableName>            
:
</tableName>

XUDFS.XRow(xdoc, rowName, isTree, nodeName, nodeValue, options)
该 xrow 标量 UDF 在生成的 XML 文档中创建一个表行 XML 节点。它还创建表行的第一列,这一列被看作该行的标识。作为输入参数传入的文档句柄 xdoc 返回给调用者。该 UDF 使用下列输入参数:

xdoc
表示要被更新的 XML 文档句柄的整数。

rowName
表示行的文本名称的 VARCHAR。它必须是一个有效的 XML 节点名称。

isTree
一个整数,表示该行的第一列是唯一的并且必须考虑对随后具有相同列值的行进行聚合。如果希望进行聚合,则应该传入值 1;否则,请使用值 0。更多关于聚合的信息,请参阅 聚合 — 它是如何工作的?

nodeName
表示要添加的新行列 XML 节点名称的 VARCHAR。它必须是一个有效的 XML 节点名称。

nodeValue
表示新节点(如果有的话)的文本值 VARCHAR。

options
表示所采用的值格式化类型的整数。如果要去除节点值的结尾空白,则请指定值 1;否则请指定值 0。

调用这一 UDF 之后将下列 XML 插入到 XML 文档:

            <rowName>            
  <nodeName>nodeValue</nodeName>
  :
</rowName>

XUDFS.XTree(xdoc, treeName, nodeName, nodeValue, options)
该 xtree 标量 UDF 用于表示考虑对行的这一新列进行聚合;也就是,不应该重复出现具有相同列值的后续行。在嵌套节点用于同一行的后续列时,请使用 xtree UDF 代替 xnode UDF。该 UDF 使用下列输入参数:

xdoc
表示要被更新的 XML 文档句柄的整数。treeName 是一个表示嵌套节点的文本名称的 VARCHAR。它必须是一个有效的 XML 节点名称。

nodeName
表示要添加的新行列 XML 节点名称的 VARCHAR。它必须是一个有效的 XML 节点名称。

nodeValue
表示新节点(如果有的话)的文本值的 varchar。

options
表示所采用的值格式化类型的整数。如果要去除节点值的结尾空白,则请指定值 1;否则请指定值 0。

调用该 UDF 之后将插入下列 XML:

            <treeName>            
  <nodeName>nodeValue</nodeName>
  :
</treeName>

XUDFS.XNode(xdoc, nodeName, nodeValue, options)
xnode 标量 UDF 将一个表示新行列的节点添加到 XML 文档。作为输入参数传入的文档句柄 xdoc 返回给调用者。该 UDF 使用下列输入参数:

xdoc
表示要被更新的 XML 文档句柄的整数。

nodeName
表示要添加的新行列 XML 节点的名称 varchar。它必须是一个有效的 XML 节点名称。

nodeValue
表示新节点(如果有的话)的文本值的 varchar(或 CLOB)。

options
表示所采用的值格式化类型的整数。如果要去除节点值的结尾空白,则请指定值 1;否则请指定值 0。

调用这一 UDF 之后将下列 XML 插入到 XML 文档:

            <nodeName>nodeValue</nodeName>            

XUDFS.XGetDocumentSize(xdoc)
xgetdocumentsize 标量 UDF 将生成的 XML 文档的大小作为整数返回。该 UDF 使用下列输入参数:

xdoc
表示要被查询的 XML 文档句柄的整数。

XUDFS.XGetDocument (xdoc)
xgetdocument 标量 UDF 将生成的 XML 文档作为 VARCHAR 返回。该文档的最大大小是 32000 字节。对于更大的文档,请使用“伙伴”UDF xgetdocument_clob,它支持的最大大小是 1MB。请使用 xgetdocumentsize UDF 来确定使用哪个获取文档 UDF。该 UDF 使用下列输入参数:

xdoc
表示要被查询的 XML 文档句柄的整数。

XUDFS.XReleaseDocument (xdoc)
xreleasedocument 标量 UDF 释放任何同生成的 XML 文档相关的内部资源,并使该 XML 文档的句柄失效。该 UDF 返回值 0。该 UDF 使用下列输入参数:

xdoc
表示要被查询的 XML 文档句柄的整数。

聚合:它是怎样工作的?
上面给出的 XML UDF 支持特别为 Video Online 需求定制的聚合机制。这种聚合假定原始的 SQL 行由有序的列集组成,在这些列集中最里面的列集可以被嵌套进最外面的列集里,对于每个嵌套级别,列集中的第一列表示是否会嵌套。

要说明这种聚合,请考虑 图 6中所显示的表数据,该图中列被分成三个集:

  • C1 至 C2
  • C3 至 C4
  • C5 至 C6

聚合列,或者被称为 树节点,分别是 C1、C3 和 C5。各种单元的内容将表示单元值是否相同。

图 6. 使用聚合的示例的数据

  C1 C2 C3 C4 C5 C6
R1 George Mally Apple Orange Dog Cat
R2 George Mally Apple Orange Mouse Rat
R3 George Mally Banana Kiwi Cow Sheep
R4 James Laura Peach Berry Tiger Lion

请使用 XML UDF 将 图 6中的值添加到 XML 文档:

  1. 使用 isTree 参数值为 1 的 xrow UDF 添加 C1。
  2. 使用 xnode UDF 添加 C2。
  3. 使用 xtree UDF 添加 C3。
  4. 使用 xnode UDF 添加 C4。
  5. 使用 xtree UDF 添加 C5。
  6. 使用 xnode UDF 添加 C6。

以下是完成这一功能的 封装器存储过程的片段的示例:

            SET xdoc = xudfs.xrow(xdoc, 'Row_Set1', 1, 'C1', C1, 0);            
SET xdoc = xudfs.xnode(xdoc, 'C2', C2, 0);
SET xdoc = xudfs.xtree(xdoc, 'Row_Set2', 'C3', C3, 0);
SET xdoc = xudfs.xnode(xdoc, 'C4', C4, 0);
SET xdoc = xudfs.xtree(xdoc, 'Row_Set3', 'C5', C5, 0);
SET xdoc = xudfs.xnode(xdoc, 'C6', C6, 0);

为该示例生成的 XML 文档是:

            <Row_Set1>            
  <C1>George</C1>
  <C2>Mally</C2>
  <Row_Set2>
    <C3>Apple</C3>
    <C4>Orange</C4>
    <Row_Set3>
      <C5>Dog</C5>
      <C6>Cat</C6>
    </Row_Set3>
    <Row_Set3>
      <C5>Mouse</C5>
      <C6>Rat</C6>
    </Row_Set3>
  </Row_Set2>
  <Row_Set2>
    <C3>Banana</C3>
    <C4>Kiwi</C4>
    <Row_Set3>
      <C5>Cow</C5>
      <C6>Sheep</C6>
    </Row_Set3>
  </Row_Set2>
</Row_Set1>
<Row_Set1>
  <C1>James</C1>
  :
</Row_Set1>

XML UDF 的局限
上面给出的 XML UDF 虽然功能丰富,但并不适用于所有类型的 SQL 到 XML 的转换;它们只是被设计成用于处理 Video Online 转换需求,包括上面详细描述的那种聚合类型,其中行排序对于正确的聚合是至关重要的。这些 UDF 支持 XML 节点,却不支持节点特性;即,它们支持 <Node>value</Node> ,却不支持 <Node property="value">value</Node> 。可以添加这一支持,但是 IBM Video Online 解决方案不需要这一支持。

创建和使用封装器 XML 存储过程(XGetMyWishedFull)
给定一个 SQL 存储过程,它返回一个或多个有零个或多个输出参数的结果集,您就可以创建一个封装器 XML 存储过程,它是一个使用上面描述的 XML 标量 UDF 来将输出参数和结果集格式化为 XML 文档的 SQL 存储过程。

虽然可以手工实现这一点,如同 Video Online 解决方案这种情况中那样,但是有可能创建一个向导,该向导指导开发人员执行所需的步骤以选择存储过程,内省其参数和结果集,定义想要的 SQL 到 XML 映射和文档布局,然后生成所需的封装器存储过程。

图 7是封装器存储过程 XGetMyWishedFull。您可以按如下方式创建该存储过程:

            C:\> DB2 -td@ -vf XGetMyWishedFull.db2            

图 7. XGetMyWishedFull

            CREATE PROCEDURE VODD.XGetMyWishedFull (            
      IN  iMemberID INT,
      OUT oXML VARCHAR(32000) )
    LANGUAGE SQL
P1: BEGIN
  -- Declaring local storage variables for data columns
  DECLARE TITLEID INTEGER;
  DECLARE TITLENAME VARCHAR(64);
  DECLARE IMAGEFILE VARCHAR(64);
  DECLARE AVGCUSTOMERRATING FLOAT;
  DECLARE AVGCRITICRATING FLOAT;
  DECLARE MOVIERANK INTEGER;
  DECLARE DATEADDED DATE;
  DECLARE PERSONID INTEGER;
  DECLARE FIRSTNAME VARCHAR(32);
  DECLARE LASTNAME VARCHAR(32);

  -- Declaring result set locator and eof flag
  DECLARE loc_rs1 RESULT_SET_LOCATOR VARYING;
  DECLARE rs_end INTEGER DEFAULT 0;

  -- Declaring XML document handle
  DECLARE xdoc INTEGER;

  -- Declaring EOF handler
  DECLARE CONTINUE HANDLER FOR NOT FOUND SET rs_end = 1;


  SET xdoc = xudfs.xdocument('GetMyWishedFull', 3, 0, 0);

  CALL VODD.GETMYWISHEDFULL(iMemberID);

  ASSOCIATE RESULT SET LOCATOR(loc_rs1) WITH PROCEDURE GETMYWISHEDFULL;
  ALLOCATE rs1 CURSOR FOR RESULT SET loc_rs1;

  SET rs_end = 0;

  SET xdoc = xudfs.xtable(xdoc, 'Titles');

  WHILE (rs_end = 0) DO
    FETCH FROM rs1 INTO
      TITLEID,
      TITLENAME,
      IMAGEFILE,
      AVGCUSTOMERRATING,
      AVGCRITICRATING,
      MOVIERANK,
      DATEADDED,
      PERSONID,
      FIRSTNAME,
      LASTNAME;

    IF (rs_end = 0) THEN
      SET xdoc = xudfs.xrow(xdoc, 'Title', 1, 'TitleID', CHAR(TITLEID), 1);
      SET xdoc = xudfs.xnode(xdoc, 'TitleName', TITLENAME, 0);
      SET xdoc = xudfs.xnode(xdoc, 'ImageFile', IMAGEFILE, 0);
      SET xdoc = xudfs.xnode(xdoc, 'AvgCustomerRating', CHAR(AVGCUSTOMERRATING), 1);
      SET xdoc = xudfs.xnode(xdoc, 'AvgCriticRating', CHAR(AVGCRITICRATING), 1);
      SET xdoc = xudfs.xnode(xdoc, 'MovieRank', CHAR(MOVIERANK), 1);
      SET xdoc = xudfs.xnode(xdoc, 'DateAdded', CHAR(DATEADDED), 1);
      SET xdoc = xudfs.xtree(xdoc, 'Person', 'PersonID', CHAR(PERSONID), 1);
      SET xdoc = xudfs.xnode(xdoc, 'FirstName', FIRSTNAME, 0);
      SET xdoc = xudfs.xnode(xdoc, 'LastName', LASTNAME, 0);
    END IF;

  END WHILE;

  CLOSE rs1;

  SET oXML = xudfs.xgetdocument(xdoc);
  SET xdoc = xudfs.xreleasedocument(xdoc);
END P1

使用结果集定位器取数据
存储过程处理嵌套存储过程的结果集需要具备下列条件:

  • 嵌套存储过程必须使用 WITH RETURN TO CALLER 游标语法声明游标。
  • 对于要处理的每个结果集,必须使用 RESULT_SET_LOCATOR VARYING 声明语法声明结果集定位器。
  • 必须使用常规 CALL 语句语法调用嵌套存储过程。
  • 必须使用 ASSOCIATE RESULT SET LOCATOR 语法将每个声明的结果集定位器同存储过程调用相关联,在该语法中可以指定多个定位器。
  • 必须使用 ALLOCATE name CURSOR FOR RESULT SET 语句语法为每个结果集定位器分配游标。

注:不应该打开使用结果集定位器分配的游标;然而,却可以直接从它们取数据。

数据聚合
如上所述,xrow 标量 UDF 用于插入新行 XML 节点,而 xnode 标量 UDF 用于插入新的子列 XML 节点。虽然对于每个同影片相关联的演职成员(人员)都会重复影片行数据,但是生成的 XML 文档却不会重复这一信息;相反,它为该影片的每个演职人员创建 Title 节点的子节点。这种数据合并通常称为聚合。

xtree 标量 UDF 用于完成希望的数据聚合。基本上,如果重复了直到 xtree 节点的数据节点,那么就为每个唯一的 xtree 节点创建一个新的子节点。

值得注意的是还支持嵌套聚合;例如,xtree 标量 UDF 可用于人员角色标记,每个演职人员可以承担一个或多个角色,例如演员和导演。

类型强制转换
如上所述,xrow、xtree 和 xnode 标量 UDF 期望的是字符格式的数据。对于诸如数字、日期等的非文本数据,必须指定显式地强制转换成 CHAR。在向强制转换成 CHAR 时,可能会生成尾随空白;必须使用这些 UDF 的 options 参数(传递值 1)去除这些空白。

样本 XML 数据输出
图 8是对标识为 1 的成员执行 XGetMyWishedFull 存储过程的结果的摘录,可以使用下列命令执行该存储过程:

            C:\> DB2 CALL VODD.XGetMyWishedFull(1, ?)            

图 8. 来自 XGetMyWishedFull 封装器存储过程的部分 XML 结果

            <?xml version="1.0"?>            
<!DOCTYPE GetMyWishedFull>
<GetMyWishedFull>
    <Titles>
        <Title>
            <TitleID>2</TitleID>
            <TitleName>Lethal Weapon 4</TitleName>
            <ImageFile>t2.jpg</ImageFile>
            <AvgCustomerRating>4.0E0</AvgCustomerRating>
            <AvgCriticRating>3.5E0</AvgCriticRating>
            <MovieRank>189</MovieRank>
            <DateAdded>01/06/2002</DateAdded>
            <Person>
                <PersonID>1</PersonID>
                <FirstName>Mel</FirstName>
                <LastName>Gibson</LastName>
            </Person>
            <Person>
                <PersonID>11</PersonID>
                <FirstName>Shane</FirstName>
                <LastName>Black</LastName>
            </Person>
            <Person>
                <PersonID>12</PersonID>
                <FirstName>Joel</FirstName>
                <LastName>Silver</LastName>
            </Person>
            <Person>
                <PersonID>19</PersonID>
                <FirstName>Richard</FirstName>
                <LastName>Donner</LastName>
            </Person>
        </Title>
        <Title>
            <TitleID>3</TitleID>
            <TitleName>Fathers Day</TitleName>
            :
        </Title>
        :
    </Titles>
</GetMyWishedFull>

请注意 Title 节点内的 Person 节点聚合;这样就不会为每个人员项多次重复片名信息了。

处理来自存储过程的多个结果集
虽然我们的示例只返回一个结果集,但是通过使用 xtable 标量 UDF 将这些结果集合并到同一个 XML 文档,或者使用 xdocument 标量 UDF 为每个结果集创建新的 XML 文档,XML UDF 可以处理多个结果集。

图 9演示了来自将两个结果集合并到一个 XML 文档的 XML 存储过程的摘录:

图 9. 合并两个结果集

              DECLARE loc_rs1 RESULT_SET_LOCATOR VARYING;            
  DECLARE loc_rs2 RESULT_SET_LOCATOR VARYING;

  ASSOCIATE RESULT SET LOCATOR(loc_rs1, loc_rs2) WITH PROCEDURE SomeProcedure;

  ALLOCATE rs1 CURSOR FOR RESULT SET loc_rs1;
  ALLOCATE rs2 CURSOR FOR RESULT SET loc_rs2;

  SET xdoc = vo.xdocument('SomeProcedure', 0, 0, 0);

  SET xdoc = vo.xtable(xdoc, 'ResultSet1');
  WHILE (rs_end = 0) DO
    FETCH FROM rs1 INTO ?
    IF (rs_end = 0) THEN
      SET xdoc = vo.xrow(xdoc, 'Row', 1, 'Col1', CHAR(Col1), 1);
      SET xdoc = vo.xnode(xdoc, 'Col2', Col2, 0);
    END IF;
  END WHILE;
  CLOSE rs1;

  SET xdoc = vo.xtable(xdoc, 'ResultSet2');
  SET rs_end = 0;
  WHILE (rs_end = 0) DO
    FETCH FROM rs2 INTO ?
    IF (rs_end = 0) THEN
      SET xdoc = vo.xrow(xdoc, 'Row', 1, 'Col1', CHAR(Col1), 1);
      SET xdoc = vo.xnode(xdoc, 'Col2', Col2, 0);
    END IF;
  END WHILE;
  CLOSE rs2;

  SET oXML = vo.xgetdocument(xdoc);
  SET xdoc = vo.xreleasedocument(xdoc);

合并存储过程
使用多种技术将两个存储过程(SQL 和 XML)合并成一个存储过程是可能的。最简单的技术是在 XML 存储过程而不是 SQL 存储过程中声明游标,这样就完全消除了对 SQL 存储过程的需求,并且再也不需要返回结果集。

然而,让 SQL 和 XML 存储过程保持分离的一个原因是:可以通过直接调用 SQL 存储过程以 SQL 格式检索数据,或者通过调用封装器 XML 存储过程以 XML 格式检索数据。这样,如果不需要 XML 转换,那么就消除了 XML 转换的开销。

第三步:将 XML 转换成 HTML 数据片段
从 XML 数据生成 HTML 是许多书籍所讨论过的一个热门主题。它涉及编码将 XML 数据转换成所希望的 HTML 的 XSL 文档。详细论述这种转换超出了本文讨论的范围。

第四步:从 HTML 数据片段构建 Web 页面
有几种从 HTML 片段构建 HTML 页面的方法。方法的选择取决于应用程序环境和可用的工具。两种这样的方法,包括使用 Java™ Server Pages(JSP)或 Active Server Pages(ASP),将 HTML 片段嵌入模板 HTML 页面。

Video Online Web 页面布局
Video Online Web 页面是静态和动态的 HTML 数据块的结合。动态 HTML 数据块并不意味着使用动态超文本标记语言(DHTML);这些 HTML 片段中嵌入的数据本质上是动态的,必须在需要时生成。

基本 Web 页面布局如 图 10所示。

图 10. Video Online Web 页面的 Web 页面布局
基本 Web 页面布局

  • 顶部区域含有站点徽标和广告横幅、成员欢迎区以及站点/成员顶部导航控件。
  • 底部区域含有应用程序信息和法律文本。
  • 右边区域含有搜索控件以及任何同主区域中显示的内容相关的导航链接。
  • 左边区域含有最重要的成员账户信息,包括成员的推荐片名清单、保留的片名队列、感兴趣的片名清单、重要的片名,以及在成员访问站点时跟踪成员操作的活动日志。
  • 主区域含有成员请求的信息,例如各种片名列表、扩展片名信息、扩展演职人员信息等等。

Web 页面的每个区域都由静态和动态内容组合而成。静态块可以使用 ASP 服务器端 include 或 JSP include 伪指令实现。这些静态块包括诸如基本页面布局、图像、固定文本和控件之类的东西。

动态块是返回到界面层之前使用 HTML 表在控制层中合并的 HTML 片段。对于 ASP 界面层,可以使用 COM+、事务组件对象模型或 SOAP(简单对象访问协议)方法调用检索 Web 页面区域。对于 JSP 接口层,可以使用 Java Bean、Enterprise Java Bean、Java Servlet 或 SOAP 方法调用检索 Web 页面区域。

通过智能片段高速缓存优化性能
构建动态 Web 用户界面时,为了避免响应用户请求时较长的时间延迟,确保以快捷、有效的方式生成 Web 页面是很重要的。本文所勾勒出的框架意味着:要构造 Web 页面,必须从界面层向控制层提出五个请求以构建每个标准页面区域。对于每个页面区域,控制层可以向数据层提出多个请求以收集并合并组成单个页面区域的各种数据片段。

这一跨层交互的代价可能十分高昂,尤其是因为必须从 DB2 检索数据片段,将数据片段转换成 XML,然后转换成 HTML。

缓解这一潜在性能瓶颈的一个快捷方法是在控制层中实现智能高速缓存机制,在该机制中,HTML 片段可以被高速缓存以供重用,这样就避免了到数据层代价高昂的往返和多次数据转换。强调智能这个词是很重要的,因为它意味着必须保证高速缓存的片段是有效的。有多种方法通过高速缓存失效算法实现这一点,在这些算法中,特定数据被更新时会自动将某个特定的高速缓存元素变为无效。

举一个这样的例子,任何片名被添加到 Web 站点中的清单中或从中删除,Video Online 中兴趣清单就会立即变为无效。

详细论述高效的高速缓存失效算法的实现,虽然非常有趣,却超过了本文的范围。

支持文件
同本文包含在一起的 zip 文件( xmloutbound.zip)包含一组文件,这些文件用于创建所需的 Video Online 表、样本数据、SQL 和 XML 存储过程以及本文中所引用的 XML 标量 UDF 所需的 DDL 和 Win-32 DLL。该 zip 文件含有:

db2xudfs.dll
含有定制 XML UDF 的 Win32 DLL。请将该 DLL 复制到 sqllib\function 目录。

db2xudfs.db2
用于创建定制 XML UDF 的脚本。可以使用下列命令调用该脚本: db2 -tvf db2xudfs.db2

CreateTables.db2
用于创建所需表的脚本。可以使用下列命令来调用该脚本: db2 -tvf CreateTables.db2

InsertData.db2
用于使用一些样本数据来填充表的脚本。请使用下列命令调用脚本: db2 -tvf InsertData.db2

GetMyWishedFull.db2
创建 GetMyWishedFull SQL 存储过程所需的脚本。可以使用下列命令调用该脚本: db2 -td@ -vf GetMyWishedFull.db2

XGetMyWishedFull.db2
创建 XGetMyWishedFull XML 存储过程所需的脚本。可以使用下列命令调用该脚本: db2 -td@ -vf XGetMyWishedFull.db2

Cleanup.db2
删除上面创建的 Video Online 表和存储过程所需的脚本文件。可以使用下列命令调用该脚本: db2 -tvf Cleanup.db2


  • 所有 db2 脚本都有一条 CONNECT TO SAMPLE 语句,如果您希望使用另一个数据库,可以修改该语句。
  • 按原样不带保证或支持提供这些文件。
  • 需要时可以自由使用、修改或再分发这些文件。

结束语
XML 已经成为应用程序之间和应用程序的组件之间事实上的数据交换协议。DB2 支持多种抽取 XML 数据的方法,包括使用 XML 扩展器和特殊目的的 UDF;一般情况下,该选择基于所期望的事务性负载和所希望的访问协议。

本文详细论述了具有特殊目的的标量 XML UDF 在 Video Online 解决方案中的使用,在该方案中,界面层从 DB2 抽取 XML 数据片段,在控制层中使用 XSL 将其转换成 HTML,然后将它们合并起来在界面层中构建 HTML Web 页面。

关于作者
照片:Abdul Al-Azzawe Abdul H. Al-Azzawe是 IBM 位于圣何塞(San Jose)硅谷实验室的资深软件工程师,自 1990 年以来一直为 IBM 工作。他是下一代 DB2 应用程序开发工具的首席架构设计师。在此之前,Abdul 是多伦多实验室的核心 DB2 引擎开发小组的成员。Abdul 是包括 IBM Video Central for e-business 和 IBM Video Online for e-business 在内的 IBM 视频系列端到端样本解决方案的首席架构工程师。

广告位置