3.4. 映射次序
本节讨论对象-关系映射如何处理次序。
3.4.1. 同级次序, 层次次序, 和文档次序
同级(Sibling)意味着“兄妹”。就是说,同级元素或 PCDATA 是有相同父元素的元素或 PCDATA。换句话说,它们出现在同一个内容模型中。例如,如果在前面的章节中文档被表示为一棵树,这很容易的显示出那些元素是同级的: 这些元素在这个层次的第二级上,都有 A 作为它们的父元素。
A
___________________________|______________________
| | | | | | | | |
This text C makes B no sense C except as B an example
| | | |
cc bbbb cccc bb
注意在第这个层次的第三级的元素不是同级的,因为它们不共享相同的父元素。这还指出了同级次序,它是在它们的父元素中子元素出现的次序,和层次次序,它是子元素在表示文档的树中出现在的级别,二者之间的不同。不同的还有文档次序,它是元素和文本在一个 XML 文档中出现的次序。例如:
同级次序(只有一个同级元素的地方次序不显示):
A
___________________________|______________________
| | | | | | | | |
This text C makes B no sense C except as B an example
1 2 3 4 5 6 7 8 9
| | | |
cc bbbb cccc bb
层次次序:
1 A
___________________________|______________________
| | | | | | | | |
2 This text C makes B no sense C except as B an example
| | | |
3 cc bbbb cccc bb
文档次序:
A
1
___________________________|______________________
| | | | | | | | |
This text C makes B no sense C except as B an example
2 3 5 6 8 9 11 12 14
| | | |
cc bbbb cccc bb
4 7 10 13
依据 XML 规定,同级次序是重要的。实际上,这依赖于应用。例如,在以数据为中心的应用中,使用 XML 文档来传载一个对象或表,同级次序通常是无关紧要的,因为面向对象语言没有在类属性之间的次序的概念。类似的,关系数据库没有在列之间的次序的概念。所以,同级次序在下列文档中不是重要的:
<Part>
<Number>123</Number>
<Desc>Turkey wrench</Desc>
<Price>10.95</Price>
</Part>
<Part>
<Price>10.95</Price>
<Desc>Turkey wrench</Desc>
<Number>123</Number>
</Part>
它们都被映射成下列对象和表中的行:
对象 表
========================= ===================================
Table Parts
object part { -------------------------------
number = 123 ==> Number Desc Price
desc = "Turkey wrench" ------ ------------- -----
price = 10.95 123 Turkey wrench 10.95
(对此的一个主要的例外是在以数据为中心的文档必须匹配一个特定的 DTD 的时候。这在一个应用必须验证文档的时候发生,比如在它们来自未知或不被信任的来源的时候。尽管在这种情况下 XML Schemas 的“all 组”通过允许一组子元素以任何次序出现能帮上忙,但它们不支持重复子元素。)
在另一方面,在以文档为中心的应用中,通常文档是为了人的消费而设计的,同级次序是非常重要的。例如,我很可能喜欢第一个评述而不是第二个:
<Review>
<p>Ronald Bourret 是一个
<b>优秀的作家</b>。
只有<b>傻瓜</b>
才不去读他的作品。</p>
</Review>
<Review>
<p>Ronald Bourret 是一个
<b>傻瓜</b>。只有
<b>优秀的作家</b>
才不去读他的作品。</p>
</Review>
对象-关系映射可以保留同级次序,下面将会见到,尽管实际上很少有产品支持它。通过把到简单元素类型的引用映射到在表中的列,和把到复杂元素类型的引用映射成主键、外键联系,它本能的保留层次次序。在保留了层次次序和同级次序的时候就保留了文档次序。
3.4.2. 映射同级次序
因为面向对象语言没有类属性之间次序的概念,而关系数据库没有列之间次序的概念,必须与数据值独立的存储同级次序值。这么做的一种方式是介入在其中存储次序值的独立类属性和列。这么做的另一种方式在映射自身中存储次序值。
3.4.2.1. 次序属性和列
使用次序类属性和次序列来存储次序值。它们对立于数据类属性和数据列。对认定次序很重要的每个被引用的元素类型或 PCDATA 都需要一个类属性或列。例如,考虑上面的混合内容的例子。下面把在 DTD 中的同级次序映射到次序类属性:
DTD 类
=============================== ========================
class A {
String[] pcdata;
int[] pcdataOrder;
<!ELEMENT A (#PCDATA | B | C)*> String[] b;
<!ELEMENT B (#PCDATA)> ==> int[] bOrder;
<!ELEMENT C (#PCDATA)> String[] c;
int[] cOrder;
}
并接着映射到次序列:
类 表
======================== ========================================
Table PCDATA
class A { -----Column a_fk
String[] pcdata; / Column pcdata
int[] pcdataOrder; / Column pcdataOrder
String[] b; Table A / Table B
int[] bOrder; ==> Column a_pk--------Column a_fk
String[] c; \ Column b
int[] cOrder; \ Column bOrder
} \ Table C
\----Column a_fk
Column c
Column cOrder
注意在标中存储的次序类属性与它们定序的类属性是并列的。
下面的例子展示使用次序属性来保留在"makes-no-sense"例子中的同级次序。这里要注意的重要的事情是所有次序类属性共享相同的次序空间。出现在一个次序类属性中一个次序值不会出现在另一个次序类属性中。
类 表
================================= =====================================
Table PCDATA
a_fk pcdata pcdataOrder
---- ----------- -----------
1 This text 1
object a { 1 makes 3
pcdata = {"This text ", 1 no sense 5
" makes ", 1 except as 7
" no sense ", Table A 1 an example. 9
" except as", a_pk
" an example."} ==> ---- Table B
pcdataOrder = {1, 3, 5, 7, 9} 1 a_fk b bOrder
b = {"bbbb", "bb"} ---- ---- ------
bOrder = {4, 8} 1 bbbb 4
c = {"cc", "cccc"} 1 bb 8
cOrder = {2, 6}
} Table C
a_fk c cOrder
---- ---- ------
1 cc 2
1 cccc 6
尽管次序类属性最常用来维护在混合内容中的次序,它们也可以与元素内容一起使用。例如,考虑下面的元素类型定义。因为在 A 中 B 可以出现任意次,它被存储在独立的属性表中。没有次序类属性,将无法确定如何定序 B 子元素。(注意这里不能使用行次序,因为关系数据库不保证以任何特定的次序返回行。)
<!ELEMENT A (B*, C)>
3.4.2.2. 在映射中存储次序
在许多情况下,同级次序只在验证的时候是重要的;除了必须能验证一个文档之外,应用自身不关心同级次序。特别是在以数据为中心的文档中元素内容。在这种情况下,在映射自身中存储次序信息就足够了。
例如,给出下列内容模型,映射可以存储 A 的子元素的次序是 B 然后 C 然后 D 的信息:
<!ELEMENT A (B, C, D)>
特别是,限制为在映射中存储次序信息。例如,考虑下面的内容模型:
<!ELEMENT A (B?, C, B)>
构造匹配这个内容模型的文档要求软件首先决定能获得多少数据来构造 B 元素。如果只有构造一个 B 元素的足够数据,它不能第一个 B 元素,因为第二个 B 元素是必须的。
让多数软件这么麻烦的这么做是不大可能的。转而,只对组织所有相同元素类型的同级元素在一起的那些内容模型提供合理的限制。对于许多以数据为中心的内容模型这是足够的,并可以通过在映射中存储每个元素在内容模型中的位置来实现。
例如,在下列元素中同级元素的次序可以被如此映射。注意在第三个内容模型中,Author 和 Editor 二者都可以被赋予相同的次序值或不同的值;如果它们被赋予不同的值,一种类型的所有元素都都会出现在其他类型的任何元素的前面。
<!ELEMENT Part (Number, Description, Price)>
<!ELEMENT Order (Number, CustNum, Date, Item*)>
<!ELEMENT Book (Title, (Author | Editor)+, Price, Review*)>
当次序信息只存储在映射中的时候,只要内容模型包含多于一个相同类型的元素,文档的无损流通(round-tripping)就是不可能的。例如,考虑下面的内容模型:
<!ELEMENT A (B+, C)>
尽管映射可以告诉软件所有 B 元素必须出现在 C 元素起前面,它不能指定 B 元素的次序。所以,如果数据从包含这种内容模型的文档传输到数据库然后再传输回来,就不能保证 B 元素按原始文档中的次序出现。幸运的是,对以数据为中心的文档这通常不是问题。
3.5. 映射属性
如同前面所见到的,把属性映射为标量类属性。本节将讨论这种映射的详情,还有其他一些要点。
3.5.1. 映射单值和多值属性
有两种不同的属性: 单值(CDATA、ID、IDREF、NMTOKEN、ENTITY、NOTATION 和枚举)和多值(IDREFS、 NMTOKENS 和 ENTITIES)。预期的可能是,把它们映射成单值类属性(接者是列)和多值类属性(接着是属性表)。例如:
DTD 类 表
============================ ============ ===========
<!ELEMENT A (B, C)> class A { Table A
<!ATTLIST A String b; Column B
D CDATA #REQUIRED> ==> String c; ==> Column C
<!ELEMENT B (#PCDATA)> String d; Column D
<!ELEMENT C (#PCDATA)> }
和:
DTD 类 表
======================== ============== ==============
<!ELEMENT A (B, C)> class A { Table A
<!ATTLIST A String b; Column a_pk
D IDREFS #IMPLIED> ==> String c; ==> Column b
<!ELEMENT B (#PCDATA)> String[] d; Column c
<!ELEMENT C (#PCDATA)> } Table D
Column a_fk
Column d
依据 XML 信息集属性出现的次序是不重要的。例如,下列两个 XML 被认为是等同的。故此,不需要次序类属性来维护属性出现的次序,尽管这么做是完全可能的。
<A B="bbb"
C="ccc"
D="ddd"/>
<A C="ccc"
B="bbb"
D="ddd"/>
在另一方面,值出现在多值属性中的次序被认为是重要的。这同于同级元素和 PCDATA 的情况,可以使用次序类属性来维护关于在多值属性中值出现次序的信息。但是,在用于同级元素和 PCDATA 的次序类属性、和用于多值属性的次序类属性之间有一个重要的区别: 用于多值属性的每个次序类属性都有它自己的次序空间。这可以在下面的例子中见到:
XML 对象
=============== =========================
object a {
<A B="dd ee ff" b = {"dd", "ee", "ff"}
C="gg hh"/> ==> bOrder = {1, 2, 3}
c = {"gg", "hh"}
cOrder = {1, 2}
}
提醒读者注意次序类属性在对象级别不是严格必须的;可以用数组次序替代。但是,在关系数据库中是必须的,因为这里没有行次序的概念。
3.5.2. 映射 ID/IDREF(S) 属性
ID 属性用来唯一的标识在 XML 文档中的元素。使用 IDREF 和 IDREFS 属性,通过提及后面的元素的 ID 来把一个元素与另一个元素关联起来。通常在不能通过把一个元素嵌套在另一个元素之中来形成这种关联的时候这么做。例如,考虑下面的有向图:
A
/ B C
\ /
D
它可以在 XML 文档中表示为:
<A>
<B ref_d="1">
...
</B>
<C ref_d="1">
...
</C>
<D id="1">
...
</D>
</A>
ID/IDREF(S) 属性映射为主键、外键联系。例如,上面的文档可以在数据库中存储为下列表和列:
Table A
Column a_pk
...
/ / Table B Table C
Column a_fk Column a_fk
Column ref_d Column ref_d
... ...
\ /
\ /
Table D
Column a_fk
Column id
...
在数据库中存储 ID/IDREF(S) 属性的时候,数据传输软件需要小心的一件事是 ID 只保证在一个给定的 XML 文档内是唯一的。所以,如果来自多于一个文档的数据存储在相同的表中,则不能保证 ID 是唯一的。这个问题的解决是以某种方式“修饰” ID。可以通过把属性映射成两列,一列包含对于每个文档是唯一的一个值,另一列包含 ID,或者修饰 ID 自身,比如用加以唯一值的前缀来完成。
在数据从数据库传输到 XML 文档的时候存在类似的问题。如果取回的数据起源于多于一个文档,则数据传输软件需要确保 ID 值是唯一的。这可能涉及到改变一个或多个值,连同引用它们的所有 IDREF(S) 属性的值。
目前,多数产品不把 ID/IDREF 作为有别于其他属性的属性来支持。
3.5.3. 映射注记属性
在 XML 文档中使用注记(notation)来提醒应用如何处理一个元素或未分析的实体。例如,下列的 "xhtml" 注记可以告诉应用这个元素包含 XHTML 并应当使用浏览器来显示:
<Desc type="xhtml">
<html>
<head><title>Turkey wrench</title></title></head>
<body>
<p>A very <b>big</b> turkey wrench.</p>
</body>
</html>
</Desc>
注记属性和它们的值对于对象-关系映射通常没有什么意义;把它们作为简单的另一种属性来对待。
对此的唯一的例外发生在注记指示包含的文本的数据类型的时候。例如,注记“base64”可以告诉应用包含二进制数据的一个元素被编码为 Base64 (映射二进制数据到 US-ASCII 的一个子集的一种 MIME 编码)。在多数情况下,这种信息只对生成映射的软件有意义。它可以使用这种信息来映射元素类型到二进制值类属性并接着到 BLOB (二进制大对象)。在这些情况下,映射自身不使用这些信息。从元素到到 BLOB 的映射独立于注记包含数据类型信息的事实。
对此的唯一的例外是当数据传输软件复杂到基于注记值来在运行时间切换映射的时候。在这种情况下,每种可能的注记被映射成一种数据类型,接着使用它来转换数据。
3.5.4. 映射 ENTITY/ENTITIES 属性
使用 ENTITY 和 ENTITIES 属性把未分析的、外部数据(比如一个二进制文件)与 XML 文档关联起来。映射它们同任何其他属性一样,不同的是,在传输数据的时候,用实体替换属性值(在从 XML 传输数据到数据库的时候),或者可以建立一个新实体并把它的标示符存储为属性值(在从数据库传输数据到 XML 的时候)。因为未分析实体值可以动态生成,映射指定在数据库中存储值还是实体 URI 是个好主意。
因为未分析实体总是有相关的注记,在决定实体的数据类型(在映射时间或运行时间)的时候可能用到这些注记。
3.6. 可供选择的映射
在前面的章节中,我们已经描述了如何映射 DTD 到数据库。实际上,这些描述是不完整的,因为还有一些其他方式来完成映射。在本节中,我们将讨论两种最重要的替代者。
3.6.1. 映射复杂元素类型到标量类型
尽管复杂元素类型通常映射成类并接着映射成表,也可能把它们映射成标量类型。换句话说,到复杂类型的引用可以被映射成标量类属性。这种类属性的值一般是元素内容,串行化为 XML。在元素的值之作为整体才有意义并且不应该分解为更小的部分的时候这是有用的。
例如,考虑给出关于 part 的信息的一个 XML 文档。如果某个子元素被部分的用 XHTML 描述,进一步分解它可能没有意义。如同我们已经描述的那样,这将导致数据被分散到许多表中;italic 字一个表,bold 字一个表,用在 hyperlink 中的字一个表,等等。所以,最好在一个单一列中存储这些描述:
DTD 类 表
=========================== =============== ==============
<!ELEMENT Part (Num, Desc)> class Part { Table Part
<!ELEMENT Number (#PCDATA)> ==> String num; ==> Column num
<!-- Use Inline entity String desc; Column desc
from XHTML --> }
<!ELEMENT Desc (%Inline;)>
例如,下面的描述将如此存储:
<Part>
<Number>127</Number> Table Part
<Desc> Num Desc
A very <b>big</b> => --- ----------------
turkey wrench. 127 A very <b>big</b>
</Desc> turkey wrench.
</Part>
注意存储数据为 XML 确实导致数据传输软件的问题。特别是,软件不能区别标记和数据。例如,应用如何决定在下列文本中的 <b> 是一个 <b> 元素还是文本?
An example of the <b> element is <b>this element</b>.
对此的一个解决方法是在数据库中使用标签存储实际元素,和使用实体引用来存储字符:
An example of the element is <b>this element</b>.
这么做的问题是非 XML 应用不能按它们希望的那样查找数据库。
3.6.2. 映射标量类属性到属性表
尽管单值的、标量值类属性通常映射成列,它们也可以被映射成属性表。这是有用的,例如,在独立于主要表的一个表中存储 BLOB 或不经常使用的类属性。例如:
类 表
=============== ==================
class Part { Table Parts
String num; ==> Column num
String desc;
Table Descriptions
} Column num
Column desc
3.7. 结论
对象-关系映射处理所有 XML 文档,有效的映射到对象,并允许非 XML 应用使用在数据库中的数据。故此,对于以数据为中心的文档是个好主意并且(不奇怪的)在某些中间件、多数启用 XML 的数据库、和多数启用 XML 的对象服务器中用作底层模型。
应当注意所有这些产品实现了对象-关系映射轻微不同的版本,并且没有一个实现了映射中所有可能的东西。在映射的更加公用的部分中,都把复杂元素映射成类并把到元素类型的引用映射成类属性,同样的使用主键、外键对来连接表。但是,一些只对唯 PCDATA 元素映射列,另一些只对属性映射列,还有其他一些允许二者。类似的,多数这些产品不支持同级次序或混合内容,并且许多在映射期间都不允许用户改变名字。
对象-关系映射对于普通文档不是个好的选择。首先,在使用混合内容的时候它是低效的。其次,象基于表的映射一样,它不保留物理结构、注释和处理指令。
4. 生成模式
我们现在考虑如何依据对象-关系映射从 DTD 生成关系数据库模式和反之。因为通过对象-关系映射有很多可能的路径,这里的算法在每次选择时简单的选择最经常使用分支。例如,可以把到唯 PCDATA 元素的单一引用映射成一列或一个单独的属性表。因为最通用的选择是把它们映射一个列,下面的算法从这样的引用生成一列。
对于面向对象数据库,生成过程是类似的。
4.1. 从 DTD 生成关系数据库模式
通过通读 DTD 并处理每个元素类型来生成关系模式:
复杂元素类型生成带有主键列的类表。
除了在处理内容模型的时候之外忽略简单元素类型。
要处理一个类型模型:
到简单元素类型的单一引用生成列;如果这个引用是可选的(? 操作符),这个列是有空值的。
到简单元素类型的重复引用生成带有外键的属性表。
到复杂元素类型的引用生成在远端(remote)类表中的外键。
在混合内容中的 PCDATA 生成带有一个外键的属性表。
对所有被引用的元素类型和 PCDATA 随意的生成有次序的列。
要处理属性:
单值属性生成列;如果属性是可选的,这个列是有空值的。
多值属性生成带有外键的属性表。
如果一个属性有缺省值,则把它用作列缺省值。
下面的例子展示了这个过程是如何工作的。考虑下列 DTD:
DTD 表
================================================= =================
<!ELEMENT Order (OrderNum, Date, CustNum, Item*)>
<!ELEMENT OrderNum (#PCDATA)>
<!ELEMENT Date (#PCDATA)>
<!ELEMENT CustNum (#PCDATA)>
<!ELEMENT Item (ItemNum, Quantity, Part)>
<!ELEMENT ItemNum (#PCDATA)>
<!ELEMENT Quantity (#PCDATA)>
<!ELEMENT Part (PartNum, Price)>
<!ELEMENT PartNum (#PCDATA)>
<!ELEMENT Price (#PCDATA)>
在第一步,我们为复杂元素类型生成表和这些表的主键:
DTD 表
================================================= =================
<!ELEMENT Order (OrderNum, Date, CustNum, Item*)> ==> Table Order
<!ELEMENT OrderNum (#PCDATA)> Column OrderPK
<!ELEMENT Date (#PCDATA)>
<!ELEMENT CustNum (#PCDATA)>
<!ELEMENT Item (ItemNum, Quantity, Part)> ==> Table Item
<!ELEMENT ItemNum (#PCDATA)> Column ItemPK
<!ELEMENT Quantity (#PCDATA)>
<!ELEMENT Part (PartNum, Price)> ==> Table Part
<!ELEMENT PartNum (#PCDATA)> Column PartPK
<!ELEMENT Price (#PCDATA)>
在第二步,我们为到简单元素类型的引用生成列:
DTD 表
================================================= =================
<!ELEMENT Order (OrderNum, Date, CustNum, Item*)> ==> Table Order
<!ELEMENT OrderNum (#PCDATA)> Column OrderPK
<!ELEMENT Date (#PCDATA)> Column OrderNum
<!ELEMENT CustNum (#PCDATA)> Column Date
Column CustNum
<!ELEMENT Item (ItemNum, Quantity, Part)> ==> Table Item
<!ELEMENT ItemNum (#PCDATA)> Column ItemPK
<!ELEMENT Quantity (#PCDATA)> Column ItemNum
Column Quantity
<!ELEMENT Part (PartNum, Price)> ==> Table Part
<!ELEMENT PartNum (#PCDATA)> Column PartPK
<!ELEMENT Price (#PCDATA)> Column PartNum
Column Price
在最后一步,我们为到复杂元素类型的引用生成外键:
DTD 表
================================================= =================
<!ELEMENT Order (OrderNum, Date, CustNum, Item*)> ==> Table Order
<!ELEMENT OrderNum (#PCDATA)> Column OrderPK
<!ELEMENT Date (#PCDATA)> Column OrderNum
<!ELEMENT CustNum (#PCDATA)> Column Date
Column CustNum
<!ELEMENT Item (ItemNum, Quantity, Part)> ==> Table Item
<!ELEMENT ItemNum (#PCDATA)> Column ItemPK
<!ELEMENT Quantity (#PCDATA)> Column ItemNum
Column Quantity
Column OrderFK
<!ELEMENT Part (PartNum, Price)> ==> Table Part
<!ELEMENT PartNum (#PCDATA)> Column PartPK
<!ELEMENT Price (#PCDATA)> Column PartNum
Column Price
Column PartFK
生成的模式将不会同人工写的模式一样。除了命名问题(例如,一个人可能把表叫做 Orders、Items, and Parts),生成算法不能确定在 Items 和 Parts 直接的联系是多对一的。这个算法还不能识别 OrderNum 和 PartNum 可以用作主键,并且它不能决定数据类型和列长度,而 XML Schemas 解决了后者问题。尽管没有命名冲突发生或非法名字生成,但二者都是可能的。
4.2. 从数据库模式生成 DTD
生成 DTD 从一个单一的“根”表或一组根表并处理每个根表开始:
每个根表生成带有一个单一序列形式的元素内容的一个元素类型。
在表中的每个数据(非键)列生成带有唯 PCDATA 内容的一个元素类型和在序列中的一个引用;有空值的列生成可选的引用。
主键和外键生成如下:
远端表用同根表相同的方式处理。
把到远端表的元素类型的引用增加到序列中。
如果键是主键,则引用是可选的和重复的(* 操作符)。这是因为不能保证在外键表中有一行存在,也不能担保只有一行存在。
如果键是主键,为在这键中的每个列随意的生成唯 PCDATA 的元素类型。如果生成了它们,向序列添加到这些元素类型的引用。这只在主键包含数据时是有用的。
如果键是外键并且是有空值的,引用是可选的(? 操作符)。
在这个处理期间,用来到达表的键(如果有的话)是不处理的。这避免了算法重复建立那些在父表中建立了的元素类型。
下列例子展示这个处理是如何工作的。考虑下列数据库模式:
Table Orders
Column OrderNum
Column Date
Column CustNum
Table Items
Column OrderNum
Column ItemNum
Column Quantity
Column PartNum
Table Parts
Column PartNum
Column Price
在第一步,我们为根表(Orders)生成一个元素类型:
表 DTD
================== ===================================================
Table Orders ==> <!ELEMENT Orders ()>
Column OrderNum
Column Date
Column CustNum
Table Items
Column OrderNum
Column ItemNum
Column Quantity
Column PartNum
Table Parts
Column PartNum
Column Price
接着,我们为数据列(Date 和 CustNum)生成唯 PCDATA 元素,并在 Orders 元素的内容模型中添加到这些元素的引用:
表 DTD
================== ===================================================
Table Orders ==> <!ELEMENT Orders (Date, CustNum)>
Column OrderNum
Column Date <!ELEMENT Date (#PCDATA)>
Column CustNum <!ELEMENT CustNum (#PCDATA)>
Table Items
Column OrderNum
Column ItemNum
Column Quantity
Column PartNum
Table Parts
Column PartNum
Column Price
现在我们为主键(OrderNum)生成一个唯 PCDATA 元素并向内容模型添加到它的一个引用:
表 DTD
================== ===================================================
Table Orders ==> <!ELEMENT Orders (Date, CustNum, OrderNum)>
Column OrderNum <!ELEMENT OrderNum (#PCDATA)>
Column Date <!ELEMENT Date (#PCDATA)>
Column CustNum <!ELEMENT CustNum (#PCDATA)>
Table Items
Column OrderNum
Column ItemNum
Column Quantity
Column PartNum
Table Parts
Column PartNum
Column Price
接着为主键被导出到其中的表(Items)增加一个元素类型,同样在它的内容模型中增加到它的一个引用:
表 DTD
================== ===================================================
Table Orders <!ELEMENT Orders (Date, CustNum, OrderNum, Items*)>
Column OrderNum <!ELEMENT OrderNum (#PCDATA)>
Column Date <!ELEMENT Date (#PCDATA)>
Column CustNum <!ELEMENT CustNum (#PCDATA)>
Table Items ==> <!ELEMENT Items()>
Column OrderNum
Column ItemNum
Column Quantity
Column PartNum
Table Parts
Column PartNum
Column Price
我们以相同的方式处理在远端(Items)表中的数据和主键列:
表 DTD
================== ===================================================
Table Orders <!ELEMENT Orders (Date, CustNum, OrderNum, Items*)>
Column OrderNum <!ELEMENT OrderNum (#PCDATA)>
Column Date <!ELEMENT Date (#PCDATA)>
Column CustNum <!ELEMENT CustNum (#PCDATA)>
Table Items ==> <!ELEMENT Items(ItemNum, Quantity)>
Column OrderNum
Column ItemNum <!ELEMENT ItemNum (#PCDATA)>
Column Quantity <!ELEMENT Quantity (#PCDATA)>
Column PartNum
Table Parts
Column PartNum
Column Price
接着为外键所对应的表(Parts)增加一个元素:
表 DTD
================== ===================================================
Table Orders <!ELEMENT Orders (Date, CustNum, OrderNum, Items*)>
Column OrderNum <!ELEMENT OrderNum (#PCDATA)>
Column Date <!ELEMENT Date (#PCDATA)>
Column CustNum <!ELEMENT CustNum (#PCDATA)>
Table Items <!ELEMENT Items(ItemNum, Quantity, Parts)>
Column OrderNum
Column ItemNum <!ELEMENT ItemNum (#PCDATA)>
Column Quantity <!ELEMENT Quantity (#PCDATA)>
Column PartNum
Table Parts ==> <!ELEMENT Parts()>
Column PartNum
Column Price
最后,我们处理外键表(Parts):
表 DTD
================== ===================================================
Table Orders <!ELEMENT Orders (Date, CustNum, OrderNum, Items*)>
Column OrderNum <!ELEMENT OrderNum (#PCDATA)>
Column Date <!ELEMENT Date (#PCDATA)>
Column CustNum <!ELEMENT CustNum (#PCDATA)>
Table Items <!ELEMENT Items (ItemNum, Quantity, Parts)>
Column OrderNum
Column ItemNum <!ELEMENT ItemNum (#PCDATA)>
Column Quantity <!ELEMENT Quantity (#PCDATA)>
Column PartNum
Table Parts ==> <!ELEMENT Parts(PartNum, Price)>
Column PartNum <!ELEMENT PartNum (#PCDATA)>
Column Price <!ELEMENT Price (#PCDATA)>
同于前面章节的情况,生成的 DTD 不同于人工建立的。尽管这里唯一的问题是有关名字的,这个算法不能识别次序列或属性表。
5. 映射 XML 模式到数据库
多数 XML 模式语言可以用对象-关系映射来映射到数据库。准确的映射依赖于语言。DDML、DCD 和 XML Data Reduced schemas 可以用几乎与 DTD 一致的方式来映射。对 W3C Schemas、Relax、TREX 和 SOX 的映射表现的更加复杂。我还不清楚 Schematron 能否映射。
在 W3C Schemas 的情况下,能获得到对象模式并接着到数据库模式的一个完整映射。简单的说,这种把复杂类型映射到类(带有复杂类型扩展映射成继承),并把简单类型映射成标量类型(尽管许多细节丢失了)。“all”组都象无序的序列那样来对待,而替换(substitution)组象选择那样对待。最后,多数同一性约束都被映射成键。详情请参见 http://www.rpbourret.com/xml/SchemaMap.htm。
6. 有关的话题
关于 XML 和数据库的更加广泛的讨论请参见:
XML and Databases (http://www.rpbourret.com/xml/XMLAndDatabases.htm)
关于 XML 数据库产品的一个适当更新的列表请参见:
XML Database Products (http://www.rpbourret.com/xml/XMLDatabaseProds.htm)