DB2 XML 编程,第 1 部分: 理解 XML 数据模型

11 篇文章 0 订阅

简介

正如 W3C 推荐标准 所指出的,XML 的一些设计目标是针对语言的应用程序开发方面:

  • “XML 将会支持各种各样的应用程序。”
  • “编写处理 XML 文档的程将会很容易。”

其他目标(比如可读性、序列化和传输)得到了许多关注,但是应用程序开发目标没有得到同样的关注。

本文是本系列的第一篇,本系列从三个层面讨论 XML 对应用程序开发的影响:

  • 第 1 部分讨论在哪些情况下在应用程序开发中使用 XML 可以使应用程序开发过程更简单、成本更低、更可移植和质量更高。在这十年中,XML 编程将使应用程序开发方式发生根本性变化,这种变化甚至可能与面向对象方法在过去十年中的影响相当。
  • 第 2 部分主要讨论数据库的角色。重点是 DB2 9(原来的代码号是 Viper)和 Viper 2 功能。将了解:
    • 新的 XML 存储和查询环境如何操作应用程序层的 XML 数据模型
    • 采用新的基于 XML 的应用程序开发体系结构之后,数据库模式如何变得更简单更自然
    • 如何按照在应用程序中查询数据的相同方式查询数据库中的 XML 数据
    • 最后,如何结合关系数据和 XML 数据,从而同时获得这两个环境的优势。
  • 第 3 部分主要关注客户机,介绍在 Web 浏览器和 Web 服务器中使用的 XML 技术:Ajax、XSLT、SVG、Comet、feed 和 mashup。学习如何在数据库中生成 feed 和 Web 服务,在应用程序层查询和组合它们,然后在客户机浏览器中显示它们。
  • 第四篇文章将所有这些技术结合在一起,并展示一个真实的示例。

XML 数据模型基本知识

在传统上,XML 用来定义业务文档的元数据。Document Object Model(DOM)用来在应用程序中操作这些元数据。如果我们查看 DOM,就会看到它为层次化 XML 数据结构提供一个对象接口,可以用 DOM API 操作这个层次结构。换句话说,DOM 可以作为对象包装器操作任何可以用 XML 表示的数据结构。

在 XML 数据模型中,将许多应用程序数据对象定义为 XML。因为 XML 是层次化的,所以很容易以自然的、人可读的格式捕捉不同数据对象之间的关系。

注意:
如果数据已经是 XML 格式的,过程的其余部分就很自然很简单了。如果数据是关系格式,仍然可以使用这种方法,但是需要在关系数据和 XML 数据之间建立双向映射。这个步骤可以使用 SQL/XML 发布和分解函数来实现。大多数关系数据库支持这些函数和其他映射技术。对于在应用程序开发中只使用 XML 作为数据模型的许多开发人员,关系数据到层次化数据(XML)的映射似乎是不必要的。但是作为开发人员,在将关系映射到数据对象时,我们总是这么做。使用 XML 数据模型的优点可能足以促使许多开发人员考虑在应用程序开发中使用 XML,即使在他们的业务模型中并不需要 XML 数据。

定义了 XML 数据模型之后,可以使用 DOM 解析器在应用程序中实例化 XML 数据模型。为了隔离应用程序代码与用来导航和操作 XML 模型的 DOM API,可以为 DOM 和 XPath 实现创建一个包装器。这个包装器也会使应用程序代码的可移植性更好。

本文提供了 一个 Java 包装器示例,您可以直接使用它,也可以用它作为模板建立自己的包装器。应用程序的业务逻辑使用包装器 API 直接操作 XML 模型。修改后的 XML 数据可以轻松地序列化,并在不同对象或多层环境(SOA)的不同层之间传递。

XML 数据模型与数据对象模型

大多数应用程序由业务对象组成,业务对象操作数据对象的层次结构。数据对象一般是业务数据的简单包装器。它们的主要用途是以受控制的方式向业务对象公开封装的数据。使用对象包装器的另一个好处是,它们可以以自然的对象层次结构表达关系表中存储的数据,从而捕捉数据之间的关系。编程工作的一个主要部分就是为应用程序业务数据创建这些对象包装器。

因为 XML 本质上维护数据结构之间的关系,所以不需要创建单独的对象层次结构来捕捉各个数据结构之间的关系。另外,XML 已经有一个标准的对象模型,Document Object Model(DOM)。这个模型的实现处理 XML 数据的构造、修改和序列化。通过结合使用 XPath 和 DOM API,很容易在业务应用程序中装载、修改和保存 XML 数据。

一个真实示例

为了更好地理解这两个模型之间的差异,我们来看看每个模型如何影响应用程序的设计和实现。

我将使用一个简单的场景演示这两种方式,这个场景要处理客户和订单信息。

数据对象模型

按照数据对象模型方式,首先需要创建包装器对象来封装客户和订单数据,见清单 1、清单 2 和清单 3。


清单 1. 创建客户

public   class  Customer
{
int customerid;
String firstname;
String lastname;
Items itemspurchased ;

Public Customer (
int custid, Connection conn)
{
Statement dbstmt
= conn.createStatement();
ResultSet dbResult 
= dbstmt.executeQuery("select fname, lname from
customer_table where customerid=custid");
customerid=custid;
SetFirstName(dbResult.getString(
1));
SetLastName(dbResult.getString(
2));
}

public String GetFirstName {return firstname;}
public Void SetFirstName (fname) {firstname=fname;}
public String GetLastName {return lastname ;}
public Void SetLastName (lname) {lastname=lname;}
public Items GetItemsList {return itemspurchased; }
public SetItemsList (list) { itemspurchased =list;}
}

清单 2. 创建 Items 类

public   class  Items
{
Hashtable list
=new Hashtable();

Public Items(
int custid,Connection conn)
{
Statement dbstmt
= conn.createStatement();
ResultSet dbResult 
= dbstmt.executeQuery("select itemid, description,
price, date from purchase_table where customerid=custid")
While (dbResult.rs.next ())
{
tempitem 
= new Item();
tempitem.SetID(dbResult. getString(
1));
tempitem. SetDescription (dbResult. getString(
2));
tempitem. Setprice (dbResult. getFloat(
3));
tempitem. SetpurchaseDate (dbResult. getString(
4));
Additem (tempitem);
}

}


public void AddItem (item oneitem) {list.put(oneitem.GetID(),oneitem);}
public Item GetItem (ItemID) {return list.get(String itemID);}
public Hashtable GetItems(){return list;}
public Items FindItemByPrice (flaot min, float max)
{
Items retList
=new Items();
for (Enumeration e=list.elements () ; e.hasMoreElements() ; )
{
item tmpItem
=(item)e.nextElement();
float price= tmpItem .GetPrice();
if(price >= min && price <=max)
{
retList.AddItem(tmpItem);
}

}

}

public Items FindItemByDate (purchaseDate) { }
}

清单 3. 创建 Item 定义

public   class  Item
{
String id;
String description;
String purchaseDate;
Float price;

Public 
void SetID (String ItemID) {id= ItemID;}
Public 
void SetDescription (String desc) { description = desc;}
Public 
void SetpurchaseDate (String pDate) { purchaseDate = pDate ;}
Public 
void Setprice (float pprice { price = pprice ;}
Public String GetID ()
{return id;}
Public String GetDescription()
{return description;}
Public 
float GetPrice(){return price;}
}

现在可以在应用程序中使用这些数据对象来管理底层数据。


清单 4. 在应用程序中操作数据对象

Customer customer  =   new  Customer (custid,dbConnection)
customer.SetItemList (
new  Items(custid , dbConnection)) ;
Items list
= customer.GetItemsList(). FindItemByPrice( 15.0 , 25.50 );
for  (Enumeration e = list.elements () ; e.hasMoreElements() ; )
{
System.out.println(((item)e.nextElement()).GetDescription());
}

在上面的示例中,我们发现数据对象的代码比业务逻辑需要的代码多得多。另外,因为包装器对象隐藏了底层业务数据之间的关系,所以包装器对象 API 必须有良好的文档记录,应用程序开发人员才能了解如何正确地使用它们。

在数据对象模型中,很容易在对象层次结构之间进行简单的导航,但是必须为每个搜索条件实现高级搜索和导航功能(例如 FindItemByPrice)。

XML 数据模型

因为包装器的主要用途是封装业务数据,所以可以用 XML 数据模型替换它们。


清单 5. XML 数据模型

< Customer  customerid  =""  firstname =""  lastname =""   >
< Items >
< Item  ID =""  description =""  purchaseDate =""  price =""   />
</ Items >
</ Customer >

现在,假设数据库中的数据已经存储为 XML 格式:

  • 客户数据存储为 <Customer customerid ="" firstname="" lastname="" />
  • 商品数据存储为 <Item ID="" description="" purchaseDate="" price="" />

那么,对于任何给定的客户,我们只需获取客户 XML 并在其中插入查询的商品列表。

重写应用程序代码,让它使用 XML 模型保存客户和商品信息。为了创建和操作这个 XML 数据模型的实例,我们将使用 DOM 包装器类 XMLParse(见 下载 一节)。

第一种情况 —— 数据在数据库中存储为 XML 格式


清单 6. 重写应用程序来使用 XML 模型
1 . Statement dbstmt =  conn.createStatement();
2 . ResultSet dbResult  =  dbstmt.executeQuery( " select custXML from
customer_table where customerid = custid " );

3 . XMLParse customerXML  =   new  XMLParse(dbResult. getString( 1 ));
4 . customerXML.appendNode( " /Customer " , customerXML.createNode ( " <Items/> " ))

5 . dbResult  =  dbstmt.executeQuery( " select itemXML from purchase_table
where customerid = custid " );
6 . While (dbResult.rs.next ())  {
7. Node itemnode= customerXML.createNode (dbResult. getString(1));
8. customerXML.appendNode(itemnode ,"/Customer/Items",false);
}

9 . customerXML.find( " /Customer/Items/item[@price>15.0 and @price <25.5] " , true );
10 for ( int  i = 0 ;i  <  customerXML.currentFind.getLength();i ++ {
11. System.out.println(customerXML.getValue("@description",i));
}

第一个查询(第 2 行)返回给定客户的 custXML 列中的 XML 数据。将这个 XML 字符串传递给 DOM 包装器的构造器(第 3 行),DOM 包装器进而使用 XML 解析器实例化一个代表 XML 数据的对象层次结构。

注意:因为客户 XML 中还没有任何 Items 元素(这符合我们在模型中定义的 XML 模式),所以我们创建一个新的 Items 元素(第 4 行),并把它作为子元素追加到 Customer 元素中。

 

第二个查询(第 5 行)从数据库中获取这位客户购买的商品的列表。将列表中的每个商品(第 7 行)追加到 DOM 对象层次结构中 Customer/items 路径的下面(第 8 行)。

最后,使用 XPath 在 DOM 对象层次结构中搜索给定价格范围内的所有商品(第 9 行),并输出搜索到的每个商品的说明(第 10 行)。

 

第二种情况 —— 所有数据在数据库中存储为关系形式

因为数据没有存储为 XML 格式,所以需要在查询中使用 SQL/XML 发布函数执行转换。


清单 7. 使用 SQL/XML 发布函数执行转换

1 . Statement dbstmt =  conn.createStatement();
2 . ResultSet dbResult  =  dbstmt.executeQuery( " select xmlelement( name "Customer" ,
xmlattributes(customerid as  " customerid" ),
xmlattributes(fname as  " firstname" ),
xmlattributes(lname as  " lastname" )
) from customer_table where customerid = custid " );

3 . XMLParse customerXML  =   new  XMLParse(dbResult. getString( 1 ));

5 . dbResult  =  dbstmt.executeQuery( " select xmlelement( name "items" ,
xmlelement( name  " item" ,
xmlattributes(itemid as  " id" ),
xmlattributes(description as  " description" ),
xmlattributes(price as  " price" ),
xmlattributes(date as  " purchaseDate" )
)
) from purchase_table where customerid
= custid " );
6 if  (dbResult.rs.next ())  {
7. Node itemsnode= customerXML.createNode (dbResult. getString(1));
8. customerXML.appendNode(itemsnode ,"/Customer",false);
}


9 . customerXML.find( " /Customer/Items/item[@price>15.0 and @price <25.5] " , true );
10 for ( int  i = 0 ;i  <  customerXML.currentFind.getLength();i ++ {
11. System.out.println(customerXML.getValue("@description",i));
}

 所以,即使数据库中没有 XML,仍然可以用 SQL 创建关系数据的 XML 视图。另外,在查询中生成商品 XML 的同时,我们添加了外层的 Items 元素。现在,只需将商品 XML 添加到客户 XML 中。应用程序的其余部分是相同的。

与纯对象模型相比使用 XML 模型的好处

数据对象包装器在应用程序代码中占很大的比例,这会大大分散开发人员对管理数据对象的业务逻辑的注意力。另外,这些多余的代码会导致:

  • 额外的成本
  • 更多的 bug
  • 更长的应用程序开发周期
  • 难以移植的代码
  • 当数据模式中发生任何修改时,需要修改或重新生成对象层次结构
  • 代码更难维护
  • 没有内置的数据检验功能
  • 需要有更多的文档来解释包装器对象
  • 对于对象层次结构中的高级搜索和导航等功能,需要相当复杂的逻辑实现
  • 由每个业务对象处理数据序列化
  • 如果使用工具进行映射,应用程序就被限制在这种工具上,难以更换工具

通过使用 XML 编程方法,就可以取消整个包装器对象层次结构,让开发人员将注意力集中在业务逻辑上,而不会为业务数据结构分心。XML 可以给应用程序开发带来以下好处:

  • 减少代码,从而提高质量、降低成本并提高灵活性。
  • 促进 RAD 开发。
  • XPath 解析器已经内置了高级搜索和导航功能。
  • XML 模型内置了约束检查和模式检验功能。
  • 模型内置了持久化功能。任何时候都可以把 XML 数据层次结构存储到文件、字符串或流中。
  • 不需要额外的工具。
  • 这种方法向业务逻辑公开了关系和数据层次结构。在业务对象代码中,很难了解要操作的业务数据结构的格式(也就是,对业务代码隐藏了数据模型)。在关系环境中,这是必要的;但是在 XML 环境中,这可能是一个缺点。
  • 业务逻辑代码容易理解,因为 XPath 描述了数据的性质以及数据与业务结构的关系。

采用 XML 模型时所涉及的问题和解决方案

如果关系数据没有存储为 XML 格式,就必须把它们映射到 XML。尽管大多数关系数据库厂商都提供了相关工具,但是映射过程非常繁琐。但是,由于 DB2 和 Microsoft® SQL Server 等数据库服务器引入了纯 XML 功能,将 XML 数据分解并映射到关系表不再是必需的。通过使用 XQuery 和 XML 索引,现在可以在应用程序中按原样搜索和获取存储的 XML 数据,采用的方式与从数据库获取大字符对象相同。

对于在数据库中存储为纯 XML 的数据,需要了解如何用 SQL/XML 函数和 XQuery 执行查询。应该先学习简单的 XPath 搜索,然后使用复杂的 XQuery,这样比较容易掌握 XML 查询。

了解并精通 DOM API 及其实现,以及掌握如何使用 XPath 导航和搜索 XML 层次结构,可能有点儿难度。可以使用 本文附带的 helper 类 减少对 DOM API 的直接调用。这个 helper 类封装了 DOM API 并公开更自然的 API,可以从应用程序代码调用这些 API。它提供了实例化和序列化 XML 模型以及在 XML 实例中搜索和修改数据或元数据所需的所有功能。包装器类还在必要时处理 XSL 转换、名称空间和模式检验。

在应用程序的业务逻辑中直接嵌入 DOM API 是一种低效率的做法,因为对 XML 模式的任何修改都要求对应用程序代码做大量修改。使用许多 API 调用在层次结构中导航;这会降低代码的可读性。导航或修改的数据对象不如使用用户定义的对象包装器时那么明显。包装器类避免了在业务逻辑中嵌入 DOM API 调用。因为这个包装器类使用 XPath 导航 DOM,所以如果对模式的任何修改影响到应用程序代码,那么只需修改应用程序中受影响的包装器 API 调用中的 XPath 字符串。另外,因为 XPath 指出了要操作的 XML 节点在层次结构中的位置,所以应用程序代码的可读性非常高。

结束语

业务应用程序主要关注创建、操作、存储和表示业务数据。为业务数据建立数据对象包装器是为了让业务逻辑更容易处理业务数据。但是,创建和维护这些数据对象包装器的成本很高,会使开发人员过分关注数据处理逻辑,而分散了对业务逻辑的注意力。

通过使用 XML 数据模型,就可以取消整个数据对象包装器层次结构,让开发人员将注意力集中在业务逻辑上,而不会为业务数据管理分心。通过使用 DOM 包装器类,应用程序代码可以与 DOM API 隔离开。使用 XPath 进行导航可以明确指出操作的业务数据中的关系,使应用程序代码更容易理解。

在理想情况下,数据应该在数据库中存储为纯 XML;但是即使数据存储在关系表中,仍然可以在应用程序中先将数据转换为 XML,然后再处理数据,这种做法在许多情况下是有意义的。

如果包装在对象层次结构中的数据结构可以用 XML 进行格式化,而且对象层次结构的主要用途是操作这些数据结构并向业务逻辑公开它们,那么可以考虑用 DOM 替代包装器对象层次结构。

本系列 的第 2 部分中,学习如何在自己的 DB2 应用程序中使用 XML 应用程序体系结构。

下载

描述名字大小下载方法
用于 Java 的 DOM 和 XPath 包装器类XMLParse.java30KBHTTP
关于下载方法的信息


参考资料

学习

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值