当行与列不能满足需要时


使用原生XML(native XML)特性处理不断变化的数据结构。

在处理不完全符合关系模型要求的数据或者应用程序必须处理随时间而改变的数据结构时,你会怎么做呢?

在本专栏中,我将阐明我是如何使用Oracle XML DB的特性来创建一个原型系统的,它无需重新编码便能适应新的及改变了的数据结构。我还将介绍Oracle的DevTrends网站上的工作示例代码。

Oracle XML DB是Oracle9i第二版(9.2.0.2或更高版)的一组特性,它为存储、定位和查询XML文档提供了原生支持。

应用程序要求

该原型系统将存储并转发政府机构所使用的管理文档(表格、状态报告、简报等)。很高兴这些文档将用XML进行编码。但仍存在着一系列难以应对的需求:

1.这些文档的数量、类型及内容经常改变。

2.该系统必须同时处理新旧两种文档类型。

3.这些文档有时含有多信息文本(rich text),包括HTML标记。

4.数据是在多个不同位置输入的,所以我需要信息域帮助解决数据分布问题。

5.将来系统迟早要支持数字签名。

设计要点

即使该系统处理的文档类型会随时间变化,我仍然希望能够在存储XML文档时对它们进行验证。因此,每一种文档类型都需要对定义它的相应XML模式进行注册。

使用Oracle9i XML DB可以将XML文档分解成关系对象(结构化存储)或者只是将这些文档整个存储为CLOB(非结构化存储)。两种方法都利用了原生的XMLType服务器类型,从而支持XPath表达式与访问方法。

因为该系统必须能够适应新的文档类型而不需改变代码,所以文档将存储为CLOB。

但只存储XML文档自身是不够的。处理文档验证的代码库以及系统的持久性、同步和管理取决于某些其他数据元素,如全局主关键字、日期/时间标记、最初位置及正在处理的文档类型。这些元素,即文档属性,必须与在不同位置间发送的XML文档共存,所以需要一个属性模式来包含它们。

有两种方法可以将XML文档同文档属性关联起来。一种是简单地将文档属性元素包含在各个文档模式之中。这种方法提供了一种相当扁平的结构,它使XPath表达式较短,但我却不能处理别人创建的模式。另一种方法是将XML文档包装在另一个含有所需属性的XML文档中。这种方法需要分别验证包装文档和内容文档,但它会提供所需要的灵活性。

创建XML模式

我的工作将从创建既能表示包装文档又能表示内容文档的XML模式开始。

首先从包装文档开始,创建一个XML模式,它包含所需的全部文档属性和一个用于内容文档的占位符(利用任一元素类型)。所有的文档属性都根据"属性"元素进行分组。我可以用KeyName元素为文档命名以便检索,Schema和Element元素指示Content元素中所包含的文档类型。

通过Oracle9i XML DB,可以使用一组特定的模式注释对设置内部存储的方式进行定制。我可以指定每个元素的存储类型,为所创建的表和类型提供数据库名。

在这样的情况下,我想混合使用结构化的XML文档存储与非结构化的XML文档存储,将内容存储为原生XML,将文档属性存储为对象关系类型。我将为DOCUMENTS设定defTable属性,并给出各Properties元素的SQLName值。对于Content元素,我打算将存储类型设为CLOB,它表示非结构化的XML存储。当此模式向数据库注册时,XML DB将依照模式注释创建对象关系类型。

接下来,为了验证置于Content元素中的文档的各个类型,还需要为各个文档类型准备一个XML模式。

我想跟踪该系统所能处理的所有内容类型,所以我会创建一个存储程序来注册内容模式。该程序会记录内容类型名和TYPES表中的其他细节,然后向XML DB注册该模式。因为我在上一步中已经为这些文档创建了CLOB存储,所以当我使用该程序为内容注册任何一种模式时,该程序都会告诉XML DB不要创建任何新表或对象类型:

Dbms_xmlschema.registerSchema(
schemaURL => schema_url,
schemaDoc => schema,
local => true,
genTypes => false,
genTables => false)

插入数据

现在我将插入一个新文档。为了更轻松,我要创建一个存储程序,它将1)检查文档内容是否已注册,2)验证文档,3)用一般XML包装文件包装文档,并填充属性元素,4)将文档插入数据库。要插入一个新文档,只需要像下面这样调用该程序:

dtx_doc.save(xmltype(
getDocument('incident1.xml')),
'Incident-001')

getDocument()调用是一个实用程序,它基于Oracle9i XML数据库开发人员指南附录G中的示例代码。它作为一个CLOB从文件系统载入文档。第二个参数save()的代表KeyName元素,将被用于在文档上建立索引。

一个突出的XML DB特性是能够在XML-文档存储表上创建基于函数的索引,如下所示:

CREATE INDEX ix_keyname
ON documents d (d.extractValue(
'/Document/Properties/KeyName'));

检索数据

现在可以对DOCUMENTS表运行基于XPath的查询,但如果创建一个XML文档存储的关系视图,如清单 1所示,就能通过标准SQL来访问结构化的存储,那么查询就会更容易。这样也可以更轻松地查询文档属性。

现在可以直接查询该视图,以查看文档属性。

清单 2 中的Print_table()过程是Tom Kyte(asktom.oracle.com)编写的一个实用程序,它可对查询进行纵向格式化。 它与本专栏的示例代码一起放在了网上。

除了通过SQL调用检索数据外,也可以利用内建于该数据库中的Oracle XML DB HTTP协议服务器检索文档。如果你的数据库监听程序正在运行,你可以在浏览器中输入以下URL,以查看插入文档的完整XML:

http://localhost:8080/oradb/
DTXDOCS/DOCUMENTS

DTXDOCS是我的数据库用户名。我也可以在URL中置入一个XPath查询,并指定一个XML样式表,它会把XML转换为格式优美的HTML文档:

http://localhost:8080/oradb/DTXDOCS/
DOCUMENTS/ROW/Document
[Properties/Subject='Incident-001']
?transform=/public/dtxdocs/incident.xslt
&contenttype=text/html

示例代码

在DevTrends网站上,我已经放置了用于说明本文简述概念的示例代码。

其中的说明脚本会告诉你我创建用于应用程序的内部工作的单一代码库以及根据需要向系统注册新文档类型的方法。由于我在实际文档中没有使用结构化的存储,所以这种方法也可能多少会有助于解决模式发展的问题。

Cameron O'Rourke ([email protected]) 担任Oracle 技术专家已超个11年。

Published At
Categories with 数据库类
Tagged with
comments powered by Disqus