4.2
将文档结构映射成数据库结构
为了能够在 XML 文档和数据库之间传递数据,有必要将文档的结构映射成数据库的结
构,反之亦然,这种映射关系又分两类:模板驱动和模型驱动
4.2.1 以模板驱动的映射
以模板驱动的映射 , 这种映射没有预先定义文档结构和数据库结构之间的映射关系
,而是使用将命令语句内嵌入模板的方法,让数据传输中间件来执行该模板。例如,考虑下面
的模板(注意该模板并不适应与所有的产品),在 <SelectStmt> 元素中内嵌了 SELECT 选
择:
<?xml version="1.0"?>
<FlightInfo>
<Intro>The following flights have available seats:</Intro>
<SelectStmt>SELECT Airline, FltNumber, Depart, Arrive FROM Flights</Se
lectStmt>
<Conclude>We hope one of these meets your needs</Conclude>
</FlightInfo>
当数据传输中间件处理到该文档时 , 每个 SELECT 选项都将被他们各自的结果所替换,
得到下面的 XML 格式:
<?xml version="1.0"?>
<FlightInfo>
<Intro>The following flights have available seats:</Intro>
<Flights>
<Row>
<Airline>ACME</Airline>
<FltNumber>123</FltNumber>
<Depart>Dec 12, 1998 13:43</Depart>
<Arrive>Dec 13, 1998 01:21</Arrive>
</Row>
...
</Flights>
<Conclude>We hope one of these meets your needs</Conclude>
</FlightInfo>
这种以模板驱动的映射方法相当灵活。例如,一些产品允许你在最后的结果中替换
你想要的内容 -- 包括在 SELECT 中使用参数 -- 而不是象上面的例子中简单地格式化结果。
另外它还支持使用编程结构例如循环和条件判断结构。还有就是它支持通过 HTTP 的传递
参数。
目前,以模板驱动的映射仅仅只支持从一个关系数据库转换成 XML 文档的情况。
4.2.2 以模型驱动的映射
在以模型驱动的映射模式中 , 它的原理就是利用 XML 文档中的数据模型的结构显性或
隐性地将其映射成数据库的结构,反之亦然。它的缺点是灵活性不如模板驱动方式,但是优点
是简单易用,这是因为它是基于具体的数据模型来进行映射的,通常它能够自己完成很多地转
换工作,从而简单易用。因为将数据从数据库转换成 XML 的工作是根据单一的一个模型(模型),
所以通常在这种方式下还要综合搭配 XSL 来提供灵活性。
在 XML 文档中有两种模型是非常普遍的。第一种是被许多中间件包在转换 XML 文档成
关系数据库数据所使用到的模型,就是将 XML 文档当成一个单独的表( Table )对象或则一系列表对
象。也就是说,真正的 XML 文档必须类似于下面的格式,如果是单一的表对象的话, <databa
se> 元素就不需要出现了
<database>
<table>
<row>
<column1>...</column1>
<column2>...</column2>
...
</row>
...
</table>
...
</database>
其中的 "table" 可理解为单个的结果集 ( 当数据是从数据库往 XML 中传输时 ) 或则是
一个单独的表对象或则一个可更新的视图( view ) ( 当数据是从 XML 往数据库传输时 ) 。如果数
据是来自多个结果集的描述中 ( 当数据来自数据库中时 ) 或则 XML 的文档包含有更深层次的嵌套
元素,有必要表现成一系列表对象 ( 当数据要转换到数据库中时 ) ,那么类似与上面例子那么
简单的传输是不可能的。
第二种普遍的数据模型是 XML 文档种的对象树,在这种模型下,元素通常对对应了
一个对象或则属性或则 PCDATA 对象。这种模型直接映射成面向对象的数据库和树状结构
数据库,当然借助传统的对象 - 关系映射技术和 SQL 3 对象视图也可以映射成关系数据库。要注意
的是,这种模型并不是文档对象模型 (DOM) , DOM 是指文档本身是个模型,而不是指文档中的数
据。
举例,在上面介绍的销售单文档可以被看成是有 5 个类的对象树 ---Orders, SalesO
rder,
Customer, Line, and Part -- 入下图所示:
Orders
|
SalesOrder
/ | /
Customer Line Line
| |
Part Part
当一个 XML 文档模型化处理成一个对象树时,对元素和对象不需要什么特殊的要求。
例如,如果一个元素只包含有 PCDATA ,例如销售单文档中的 CustName 元素,它能够被看做一个
属性进行处理(就是仅仅只包含有单独的数值)。简单来说,有时将混合元素或则元素内容
模型化处理成属性是非常有用的方法。一个现成的例子就是在销售单文档中对 Description 元素
的处理:尽管它在 XHTML 的 Form 中有混合的内容,但是将 description 元素看作一个单独的属性来
处理会更有用些,因为它的组成部分就本身而言没有什么意义。
4.3 数据类型 , 空值( Null ) , 字符集设置和其他所有的类似集
本节将探讨一些和将 XML 文档转换成数据库之间有关存储数据的内容。通常,你在选
择什么样的中间件来解决这些问题的时候是不会考虑到这些问题的,但是如果你注意到这些问
题的存在时,希望下面的讨论对与你在选择中间件时有所帮助。
4.3.1 数据类型
XML 不支持任何有意义的数据类型,除非是不能够解释的实体,所有 XML 文档中的数
据都被当成文本( text )来对待,虽然它能够用其他的数据类型来表示,例如可以表示成日期
或则整数。通常,数据转换中间件将把文本(在 XML 文档中的文本)转换成其它的数据类型(数据库
中的数据类型),反之亦然。然而,一些特定的数据类型在转换的过程中是受限制的,例如受那
些提供数据支持的 JDBC 驱动的限制。在这些众多的有可能的数据类型中,日期类型通常会导致
麻烦。数字,特别是由于国际地域不同的数字格式,也可能导致问题。
4.3.2 二进制数据
有两种比较普遍的方法将二进制数据保存到 XML 文档中:对实体不做任何编码处理和
对实体进行 Base64 编码处理 ( 一种 MIME 编码方法,可以将二进制数据影射成 US-ASCII 的子集 ) 。
对于关系数据库,这两种方法都被证明是有可能存在问题的,因为大家都知道当保存和获取二进
制数据到数据库中的规则是非常严格的,这样对中间件将有可能导致问题。另外,并没有一种标准的符
号用来说明一个 XML 文档中的元素包含有 Base64 编码数据,从而中间件可能根本就不能够识别这
种编码。最后,还有可能有些中间件在将数据存储入数据库的过程中根本就会忽略没有编码实体
中的符号或则 Base64 编码中的元素。所以,如果对你而言,二进制数据非常重要的话,请千万要
确认你的中间件是否支持二进制数据。
4.3.3 空值( Null )
在数据库世界中, null 数据意味着数据不在那。这不同与一个值为 0( 对数字类型数
据 ) 或则长度为 0( 对字符串类型 ) 。例如,假设你的数据是收集自一个气象站, 如果气象站的温
度计出毛病了,那么你的数据库中将存储一个 null 值而不是一个 0 ,值为 0 完全是另外一回事了
。
XML 也支持空值的概念,可以通过设置元素的类型和属性来实现。如果元素类型或属
性的值为 null , XML 的处理方法是简单地不将其包含到文档中。但是对数据库来说,空的元素
或则包含 0 长度字符串的属性并不意味着 null :他们的值是长度为 0 的字符串。
当将一个 XML 文档结构映射成数据库或则反过程中,你必须特别注意那些可选的数据
类型和本来表示空值的属性。如果不这么做的话,结果将是可能出现插入错误 ( 当将数据转换
到数据库中时 ) 或则非法文档错误 ( 当将数据从数据库读出时 ) 。
因为 XML 中相对与数据库而言在对符号意义的申明有更好的灵活性 --- 具体来说,
就是 XML 用户愿意将空元素或则包含长度为 0 内容的属性认为是 "null" -- 你必须根据这个考虑选
择什么样的中间件来处理这个问题。一些中间件提供给用户自定义在 XML 文档中什么标志是表示
"null" 的。
4.3.4 字符集设置
根据定义,一个 XML 文档能够包含任何 Unicode 字符,除了一些特殊的控制字符。但
是不幸的是,许多数据库都限制或则不支持 Unicode 并且需要一些特殊的配置才能够处理非 ASC
II 编码的数据字符。如果你的数据包含有非 ASCII 字符,那么请确保你的数据库和中间件是否能够
处理这些字符集。
4.3.5 处理指令( Processing Instructions )
处理指令不是 XML 文档中的 “ 数据 ” 部分,目前许多中间件都不能够正常的处理它们
。问题是这样的,尤其是在一个严格的将 XML 文档结构映射成数据库结构中,处理指令通常
是很难处理的,因为题目可以出现在文档的任何位置,于是,中间件就非常困难的需要判
断将它们保存到什么位置和读取的时候取回到什么位置。如果处理指令和文挡的 "round-t
ripping" 对你而言是非常重要的话,你就必须确保你选择的中间件能够处理这个问题。
4.3.6 存储标志( Storing Markup )
在 4.2.2 小节中提到,有时候直接将包含有元素或则混合内容的元素不进行进一步的
解析直接保存到数据库中是非常有用的。最普遍的实现方法是简单的把这个标志本身直接保
存到数据库中。不幸的是,这将带来另外一个问题,当从数据库中读取这些数据时:将很难
判断数据库中的标志到底是真是假,特别是一些由 lt 和 gt 转义的字符。
例如下面的描述:
<description>
<b>Confusing example:</b> <foo/>
</description>
保存到数据库中将变成这样:
<b>Confusing example:</b> <foo/>
这时数据库将不能够判断 <b> 和 <foo> 是标志还是文本了。解决方法有以下几种,例如
将标志的符号使用其它非标志符号替代,但是这时你要非常的小心,因为也许别的运用程
序在使用这些数据时就会出现不兼容的现象。例如,如果你想查询数据库中的小于号 ("<") 和
lt 标志 ("<") 时就要特别小心了。
4.4 从数据库的结构生成 DTDs 和逆反过程
在 XML 文档和数据库之间转换数据的一个普遍问题是:如何从数据库的结构生成 D
TDs 和其逆反过程。简而言之,目前有许多软件都提供了可以直接使用的操作功能,但是它
产生的结果对许多用户来说用处和帮助不大,也许没有多少人喜欢。
例如,下面的过程(已经简化过的)就是从一个 XML 文档到关系数据库中生成 DTD 的
:对每一种包含有元素或则混合内容的元素类型,新建立一个 table 和一个主关键字段。
对混合内容种的每一个元素,建立一个分开的 table, 在其中保存 PCDATA, 通过主关键
字连接到父表中。
对于元素类型中每个有单一数值的属性和只包含有 PCDATA 内容的子元素类型在该 ta
ble 中新建立一列(字段)。如果子元素类型或则属性是可选的,让该字段允许为空。
对于每个有多值的属性或则多仅含有 PCDATA 内容的子元素类型,再建立一个分开的
table 来保存他们的值,通过它们的父表的主关键字连接到父表。
对于每个子元素,这些子元素本身还有元素或则混合内容,使用父表中的关键字将
父元素表连接到子元素表中。
而下面则是一个从关系数据库的结构生成 XML 文档的过程(简化过的):
对每个 table ,新建一个元素。
对表中的每列,建立一个属性或则只含 PCDATA 的子元素
对每个包含有在主键 / 外键关键字关系中主键值的列,新建一个子元素。
不幸的是,在这些过程中存在许多缺陷。例如,其中没有实现对数据类型的预先定
义和在 DTD 中没有实现对列长度的预先定义的方法。因为任何的预先定义,例如通过读一个
示例文档,当读取其他 “ 类型 ” 的文档或则其他文档中包含有超过字段长度内容的文档时,
就会发生错误。 ( 解决这个问题的办法时使用 XML schema 文档中数据类型定义 ) 简单来说,当从一
个关系结构中生成 DTD 时,是没有办法预先判断子元素 “ 应该 ” 出现的顺序或则类似数据库中的
行标识。
尽管有这些陷,根据数据库结构生成 DTDs 的软件能够给我们带来了一个很好的开端
,特别是对与那些非常庞大和复杂的系统
为了能够在 XML 文档和数据库之间传递数据,有必要将文档的结构映射成数据库的结
构,反之亦然,这种映射关系又分两类:模板驱动和模型驱动
4.2.1 以模板驱动的映射
以模板驱动的映射 , 这种映射没有预先定义文档结构和数据库结构之间的映射关系
,而是使用将命令语句内嵌入模板的方法,让数据传输中间件来执行该模板。例如,考虑下面
的模板(注意该模板并不适应与所有的产品),在 <SelectStmt> 元素中内嵌了 SELECT 选
择:
<?xml version="1.0"?>
<FlightInfo>
<Intro>The following flights have available seats:</Intro>
<SelectStmt>SELECT Airline, FltNumber, Depart, Arrive FROM Flights</Se
lectStmt>
<Conclude>We hope one of these meets your needs</Conclude>
</FlightInfo>
当数据传输中间件处理到该文档时 , 每个 SELECT 选项都将被他们各自的结果所替换,
得到下面的 XML 格式:
<?xml version="1.0"?>
<FlightInfo>
<Intro>The following flights have available seats:</Intro>
<Flights>
<Row>
<Airline>ACME</Airline>
<FltNumber>123</FltNumber>
<Depart>Dec 12, 1998 13:43</Depart>
<Arrive>Dec 13, 1998 01:21</Arrive>
</Row>
...
</Flights>
<Conclude>We hope one of these meets your needs</Conclude>
</FlightInfo>
这种以模板驱动的映射方法相当灵活。例如,一些产品允许你在最后的结果中替换
你想要的内容 -- 包括在 SELECT 中使用参数 -- 而不是象上面的例子中简单地格式化结果。
另外它还支持使用编程结构例如循环和条件判断结构。还有就是它支持通过 HTTP 的传递
参数。
目前,以模板驱动的映射仅仅只支持从一个关系数据库转换成 XML 文档的情况。
4.2.2 以模型驱动的映射
在以模型驱动的映射模式中 , 它的原理就是利用 XML 文档中的数据模型的结构显性或
隐性地将其映射成数据库的结构,反之亦然。它的缺点是灵活性不如模板驱动方式,但是优点
是简单易用,这是因为它是基于具体的数据模型来进行映射的,通常它能够自己完成很多地转
换工作,从而简单易用。因为将数据从数据库转换成 XML 的工作是根据单一的一个模型(模型),
所以通常在这种方式下还要综合搭配 XSL 来提供灵活性。
在 XML 文档中有两种模型是非常普遍的。第一种是被许多中间件包在转换 XML 文档成
关系数据库数据所使用到的模型,就是将 XML 文档当成一个单独的表( Table )对象或则一系列表对
象。也就是说,真正的 XML 文档必须类似于下面的格式,如果是单一的表对象的话, <databa
se> 元素就不需要出现了
<database>
<table>
<row>
<column1>...</column1>
<column2>...</column2>
...
</row>
...
</table>
...
</database>
其中的 "table" 可理解为单个的结果集 ( 当数据是从数据库往 XML 中传输时 ) 或则是
一个单独的表对象或则一个可更新的视图( view ) ( 当数据是从 XML 往数据库传输时 ) 。如果数
据是来自多个结果集的描述中 ( 当数据来自数据库中时 ) 或则 XML 的文档包含有更深层次的嵌套
元素,有必要表现成一系列表对象 ( 当数据要转换到数据库中时 ) ,那么类似与上面例子那么
简单的传输是不可能的。
第二种普遍的数据模型是 XML 文档种的对象树,在这种模型下,元素通常对对应了
一个对象或则属性或则 PCDATA 对象。这种模型直接映射成面向对象的数据库和树状结构
数据库,当然借助传统的对象 - 关系映射技术和 SQL 3 对象视图也可以映射成关系数据库。要注意
的是,这种模型并不是文档对象模型 (DOM) , DOM 是指文档本身是个模型,而不是指文档中的数
据。
举例,在上面介绍的销售单文档可以被看成是有 5 个类的对象树 ---Orders, SalesO
rder,
Customer, Line, and Part -- 入下图所示:
Orders
|
SalesOrder
/ | /
Customer Line Line
| |
Part Part
当一个 XML 文档模型化处理成一个对象树时,对元素和对象不需要什么特殊的要求。
例如,如果一个元素只包含有 PCDATA ,例如销售单文档中的 CustName 元素,它能够被看做一个
属性进行处理(就是仅仅只包含有单独的数值)。简单来说,有时将混合元素或则元素内容
模型化处理成属性是非常有用的方法。一个现成的例子就是在销售单文档中对 Description 元素
的处理:尽管它在 XHTML 的 Form 中有混合的内容,但是将 description 元素看作一个单独的属性来
处理会更有用些,因为它的组成部分就本身而言没有什么意义。
4.3 数据类型 , 空值( Null ) , 字符集设置和其他所有的类似集
本节将探讨一些和将 XML 文档转换成数据库之间有关存储数据的内容。通常,你在选
择什么样的中间件来解决这些问题的时候是不会考虑到这些问题的,但是如果你注意到这些问
题的存在时,希望下面的讨论对与你在选择中间件时有所帮助。
4.3.1 数据类型
XML 不支持任何有意义的数据类型,除非是不能够解释的实体,所有 XML 文档中的数
据都被当成文本( text )来对待,虽然它能够用其他的数据类型来表示,例如可以表示成日期
或则整数。通常,数据转换中间件将把文本(在 XML 文档中的文本)转换成其它的数据类型(数据库
中的数据类型),反之亦然。然而,一些特定的数据类型在转换的过程中是受限制的,例如受那
些提供数据支持的 JDBC 驱动的限制。在这些众多的有可能的数据类型中,日期类型通常会导致
麻烦。数字,特别是由于国际地域不同的数字格式,也可能导致问题。
4.3.2 二进制数据
有两种比较普遍的方法将二进制数据保存到 XML 文档中:对实体不做任何编码处理和
对实体进行 Base64 编码处理 ( 一种 MIME 编码方法,可以将二进制数据影射成 US-ASCII 的子集 ) 。
对于关系数据库,这两种方法都被证明是有可能存在问题的,因为大家都知道当保存和获取二进
制数据到数据库中的规则是非常严格的,这样对中间件将有可能导致问题。另外,并没有一种标准的符
号用来说明一个 XML 文档中的元素包含有 Base64 编码数据,从而中间件可能根本就不能够识别这
种编码。最后,还有可能有些中间件在将数据存储入数据库的过程中根本就会忽略没有编码实体
中的符号或则 Base64 编码中的元素。所以,如果对你而言,二进制数据非常重要的话,请千万要
确认你的中间件是否支持二进制数据。
4.3.3 空值( Null )
在数据库世界中, null 数据意味着数据不在那。这不同与一个值为 0( 对数字类型数
据 ) 或则长度为 0( 对字符串类型 ) 。例如,假设你的数据是收集自一个气象站, 如果气象站的温
度计出毛病了,那么你的数据库中将存储一个 null 值而不是一个 0 ,值为 0 完全是另外一回事了
。
XML 也支持空值的概念,可以通过设置元素的类型和属性来实现。如果元素类型或属
性的值为 null , XML 的处理方法是简单地不将其包含到文档中。但是对数据库来说,空的元素
或则包含 0 长度字符串的属性并不意味着 null :他们的值是长度为 0 的字符串。
当将一个 XML 文档结构映射成数据库或则反过程中,你必须特别注意那些可选的数据
类型和本来表示空值的属性。如果不这么做的话,结果将是可能出现插入错误 ( 当将数据转换
到数据库中时 ) 或则非法文档错误 ( 当将数据从数据库读出时 ) 。
因为 XML 中相对与数据库而言在对符号意义的申明有更好的灵活性 --- 具体来说,
就是 XML 用户愿意将空元素或则包含长度为 0 内容的属性认为是 "null" -- 你必须根据这个考虑选
择什么样的中间件来处理这个问题。一些中间件提供给用户自定义在 XML 文档中什么标志是表示
"null" 的。
4.3.4 字符集设置
根据定义,一个 XML 文档能够包含任何 Unicode 字符,除了一些特殊的控制字符。但
是不幸的是,许多数据库都限制或则不支持 Unicode 并且需要一些特殊的配置才能够处理非 ASC
II 编码的数据字符。如果你的数据包含有非 ASCII 字符,那么请确保你的数据库和中间件是否能够
处理这些字符集。
4.3.5 处理指令( Processing Instructions )
处理指令不是 XML 文档中的 “ 数据 ” 部分,目前许多中间件都不能够正常的处理它们
。问题是这样的,尤其是在一个严格的将 XML 文档结构映射成数据库结构中,处理指令通常
是很难处理的,因为题目可以出现在文档的任何位置,于是,中间件就非常困难的需要判
断将它们保存到什么位置和读取的时候取回到什么位置。如果处理指令和文挡的 "round-t
ripping" 对你而言是非常重要的话,你就必须确保你选择的中间件能够处理这个问题。
4.3.6 存储标志( Storing Markup )
在 4.2.2 小节中提到,有时候直接将包含有元素或则混合内容的元素不进行进一步的
解析直接保存到数据库中是非常有用的。最普遍的实现方法是简单的把这个标志本身直接保
存到数据库中。不幸的是,这将带来另外一个问题,当从数据库中读取这些数据时:将很难
判断数据库中的标志到底是真是假,特别是一些由 lt 和 gt 转义的字符。
例如下面的描述:
<description>
<b>Confusing example:</b> <foo/>
</description>
保存到数据库中将变成这样:
<b>Confusing example:</b> <foo/>
这时数据库将不能够判断 <b> 和 <foo> 是标志还是文本了。解决方法有以下几种,例如
将标志的符号使用其它非标志符号替代,但是这时你要非常的小心,因为也许别的运用程
序在使用这些数据时就会出现不兼容的现象。例如,如果你想查询数据库中的小于号 ("<") 和
lt 标志 ("<") 时就要特别小心了。
4.4 从数据库的结构生成 DTDs 和逆反过程
在 XML 文档和数据库之间转换数据的一个普遍问题是:如何从数据库的结构生成 D
TDs 和其逆反过程。简而言之,目前有许多软件都提供了可以直接使用的操作功能,但是它
产生的结果对许多用户来说用处和帮助不大,也许没有多少人喜欢。
例如,下面的过程(已经简化过的)就是从一个 XML 文档到关系数据库中生成 DTD 的
:对每一种包含有元素或则混合内容的元素类型,新建立一个 table 和一个主关键字段。
对混合内容种的每一个元素,建立一个分开的 table, 在其中保存 PCDATA, 通过主关键
字连接到父表中。
对于元素类型中每个有单一数值的属性和只包含有 PCDATA 内容的子元素类型在该 ta
ble 中新建立一列(字段)。如果子元素类型或则属性是可选的,让该字段允许为空。
对于每个有多值的属性或则多仅含有 PCDATA 内容的子元素类型,再建立一个分开的
table 来保存他们的值,通过它们的父表的主关键字连接到父表。
对于每个子元素,这些子元素本身还有元素或则混合内容,使用父表中的关键字将
父元素表连接到子元素表中。
而下面则是一个从关系数据库的结构生成 XML 文档的过程(简化过的):
对每个 table ,新建一个元素。
对表中的每列,建立一个属性或则只含 PCDATA 的子元素
对每个包含有在主键 / 外键关键字关系中主键值的列,新建一个子元素。
不幸的是,在这些过程中存在许多缺陷。例如,其中没有实现对数据类型的预先定
义和在 DTD 中没有实现对列长度的预先定义的方法。因为任何的预先定义,例如通过读一个
示例文档,当读取其他 “ 类型 ” 的文档或则其他文档中包含有超过字段长度内容的文档时,
就会发生错误。 ( 解决这个问题的办法时使用 XML schema 文档中数据类型定义 ) 简单来说,当从一
个关系结构中生成 DTD 时,是没有办法预先判断子元素 “ 应该 ” 出现的顺序或则类似数据库中的
行标识。
尽管有这些陷,根据数据库结构生成 DTDs 的软件能够给我们带来了一个很好的开端
,特别是对与那些非常庞大和复杂的系统