新书推介:《语义网技术体系》
作者:瞿裕忠,胡伟,程龚
   XML论坛     W3CHINA.ORG讨论区     计算机科学论坛     SOAChina论坛     Blog     开放翻译计划     新浪微博  
 
  • 首页
  • 登录
  • 注册
  • 软件下载
  • 资料下载
  • 核心成员
  • 帮助
  •   Add to Google

    >> Oracle, SQL Server与XML,XML在数据挖掘中的应用, PMML.
    [返回] 中文XML论坛 - 专业的XML技术讨论区XML.ORG.CN讨论区 - 高级XML应用『 XML 与 数据库 』 → 映射 DTD 到数据库 查看新帖用户列表

      发表一个新主题  发表一个新投票  回复主题  (订阅本版) 您是本帖的第 20351 个阅读者浏览上一篇主题  刷新本主题   平板显示贴子 浏览下一篇主题
     * 贴子主题: 映射 DTD 到数据库 举报  打印  推荐  IE收藏夹 
       本主题类别:     
     mhss 帅哥哟,离线,有人找我吗?
      
      
      等级:大一新生
      文章:1
      积分:67
      注册:2003/12/22

    姓名:(无权查看)
    城市:(无权查看)
    院校:(无权查看)
    给mhss发送一个短消息 把mhss加入好友 查看mhss的个人资料 搜索mhss在『 XML 与 数据库 』的所有贴子 引用回复这个贴子 回复这个贴子 查看mhss的博客楼主
    发贴心情 映射 DTD 到数据库

    http://mhss.nease.net/xml/dtd_map.html
    映射 DTD 到数据库
    作者:Ronald Bourret

    May 09, 2001

    翻译:寒蝉退士

    译者声明:译者对译文不做任何担保,译者对译文不拥有任何权利并且不负担任何责任和义务。

    原文:http://www.xml.com/pub/a/2001/05/09/dtdtodbs.html


    --------------------------------------------------------------------------------

    目录
    1. 概述
    2. 基于表的映射
    3. 对象-关系映射
    3.1. 基本映射
    3.1.1. 映射 DTD 到对象模式
    3.1.2. 映射对象模式到数据库模式
    3.1.3. 杂记
    3.2. 映射复杂内容模型
    3.2.1. 映射序列
    3.2.2. 映射选择
    3.2.3. 映射重复子元素
    3.2.4. 映射可选子元素
    3.2.5. 映射子组
    3.3. 映射混合内容
    3.4. 映射次序
    3.4.1. 同级次序, 层次次序, 和文档次序
    3.4.2. 映射同级次序
    3.4.2.1. 次序属性和列
    3.4.2.2. 在映射中存储次序
    3.5. 映射属性
    3.5.1. 映射单值和多值属性
    3.5.2. 映射 ID/IDREF(S) 属性
    3.5.3. 映射注记属性
    3.5.4. 映射 ENTITY/ENTITIES 属性
    3.6. 可供选择的映射
    3.6.1. 映射复杂元素类型到标量类型
    3.6.2. 映射标量类属性到属性表
    3.7. 结论
    4. 生成模式
    4.1. 从 DTD 生成关系数据库模式
    4.2. 从数据库模式生成 DTD
    5. 映射 XML 模式到数据库
    6. 有关的话题

    --------------------------------------------------------------------------------

    1. 概述
    在 XML 社团中一个常见的问题是如何把 XML 映射到数据库。本文讨论两种映射: 基于表的映射和对象-关系(基于对象)的映射。这两种映射都建模在 XML 文档中的数据、而不是文档自身。这使得这些映射对于以数据为中心的文档是良好的选择,而对于以文档为中心的文档是贫乏的选择。基于表的映射完全不能处理混合内容,而混合内容的对象-关系映射是非常低效的。

    这两种映射通常用做在 XML 文档和数据库、特别是关系数据库之间传输数据的软件的基础。它们在这方面的一个重要特征是双向的。就是说,它们可以用来从 XML 文档向数据库、从数据库向 XML 文档传输数据。 结果之一是它们可用做规范映射,在它顶上的 XML 查询语言可以被建造在非 XML 数据库上。规范映射将定义可以用类似 XQuery 的某种东西来查询的虚拟 XML 文档。

    除了在 XML 文档和数据库之间传输数据之外,对象关系映射首要角色是用在“数据绑定”之中,这是在 XML 文档和对象之间的数据集结和解散。

    2. 基于表的映射
    在下面的 XML 文档和表之间有明显的映射:

       <A>                     
          <B>
             <C>ccc</C>             Table A
             <D>ddd</D>             -------
             <E>eee</E>          C     D     E
          </B>                  ---   ---   ---
          <B>             <=>   ...   ...   ...
             <C>fff</C>         ccc   ddd   eee
             <D>ggg</D>         fff   ggg   hhh
             <E>hhh</E>         ...   ...   ...
          </B>
       </A>
       
    它叫做基于表的映射。它把文档看成一个单一的表或一组表。文档的结构必须是

       <Table>
          <Row>
             <Column_1>...</Column_1>
             ...
             <Column_n>...</Column_n>
          </Row>
          ...
          <Row>
             <Column_1>...</Column_1>
             ...
             <Column_n>...</Column_n>
          </Row>
       </Table>
    或者是

       <Tables>
          <Table_1>
             <Row>
                <Column_1>...</Column_1>
                ...
                <Column_n>...</Column_n>
             </Row>
             ...
          </Table_1>
          ...
          <Table_n>
             <Row>
                <Column_1>...</Column_1>
                ...
                <Column_m>...</Column_m>
             </Row>
             ...
          </Table_n>
       </Tables>
    同这个限制在一起的还有,列数据可以被表示为唯 PCDATA 元素(可显示的)或属性。

    这种映射的明显的优点是它的简单性。因为它匹配在关系数据库中的表和结果集的结构,基于这种映射写代码很容易、快速、缩放自如,并对特定应用非常有用,比如在数据库之间一次一个表的传输数据。

    这种映射有许多缺点;首先,它只能处理 XML 文档的非常小的子集。此外,它不保存物理结构(比如字符和实体引用,CDATA 段,字符编码,和孤立的声明)或文档信息(比如文档类型或 DTD),注释,或处理指令。

    基于表的映射通常被中间件用来在 XML 文档和关系数据库之间传输数据。它还在某些 Web 应用服务器中用来把结果集数据返回为 XML。

    3. 对象-关系映射
    由于基于表的映射只能处理 XML 文档的一个有限的子集,一些中间件工具、多数启用 XML 的关系数据库、和多数启用 XML 的对象服务器使用一个更加完善的映射,它叫做对象-关系映射。它把 XML 文档建模为特定于在文档中数据的对象的一个树,接着把这些对象映射到数据库。

    (名字“对象-关系”实际上是用词不当的 -- 更好的名字是基于对象的映射。这是因为对象可以被映射到非关系数据库,比如面向对象数据库或层次数据库, 或者简单的不去管它,在数据绑定系统中就是这么做的。但是,由于对象-关系是一个熟知的术语而且这种映射通常与关系数据库一起使用,所以这里用了这个术语。此外,所有例子都使用关系表。)

    要理解对象-关系映射,最好先看一下一些简单的例子。作为开始,注意在下面 XML 文档、对象、和在表中的行之间有明显的映射:

            XML                    对象                     表
       =============           ============           ===============

                                                         Table A
       <A>                     object A {                 -------
          <B>bbb</B>              B = "bbb"            B     C     D
          <C>ccc</C>    <=>       C = "ccc"    <=>    ---   ---   ---
          <D>ddd</D>              D = "ddd"           ...   ...   ...
       </A>                    }                      bbb   ccc   ddd
                                                      ...   ...   ...
    类似的,在下列元素类型定义、类和表模式之间也有明显的映射:

                DTD                           类                      表模式
       ======================           ============           ==========================

                                        class A {              CREATE TABLE A
                                           String B;              B VARCHAR(10) NOT NULL,
       <!ELEMENT A (B, C, D)>    <=>       String C;    <=>       C VARCHAR(10) NOT NULL,
                                           String D;              D VARCHAR(10) NOT NULL
                                        }                      )
    作为一个更复杂的例子,考虑下列 XML 文档:

       <SalesOrder>
          <Number>1234</Number>
          <Customer>Gallagher Industries</Customer>
          <Date>29.10.00</Date>
          <Item Number="1">
             <Part>A-10</Part>
             <Quantity>12</Quantity>
             <Price>10.95</Price>
          </Item>
          <Item Number="2">
             <Part>B-43</Part>
             <Quantity>600</Quantity>
             <Price>3.99</Price>
          </Item>
       </SalesOrder>
    它映射成下面的对象:

          object SalesOrder {
             number = 1234;
             customer = "Gallagher Industries";
             date = 29.10.00;
             items = {ptrs to Item objects};
          }           /   \
                     /     \
                    /       \
       object Item {       object Item {
          number = 1;         number = 2;
          part = "A-10";      part = "B-43";
          quantity = 12;      quantity = 600;
          price = 10.95;      price = 3.95;
       }                   }
    并接着映射成下面的表:

       SaleOrders
       ----------
       Number   Customer                Date
       ------   --------------------    --------
       1234     Gallagher Industries    29.10.00
       ...      ...                     ...
       ...      ...                     ...
       
       
       Items
       -----
       SONumber   Item   Part   Quantity   Price
       --------   ----   ----   --------   -----
       1234       1      A-10   12         10.95
       1234       2      B-43   600        3.99
       ...        ...    ...    ...        ...
    所有这些都是从 DTD 到关系数据库表的对象-关系映射。

    3.1. 基本映射
    对象-关系映射用两个步骤完成。首先,把 XML 模式(这里是 DTD)映射成一个对象模式,接着把对象模式映射成数据库模式。这两步映射可以随意的被结合在一起为直接的 DTD-到-数据库映射,如同今天多数软件作的那样。

    在考虑这种映射的时候,理解涉及到的对象是特定于每个 DTD 的,而不是来自 DOM 的对象是很重要的。特别是,这些对象建模在 XML 文档中的数据,而 DOM 建模 XML 文档的结构。例如,下面展示了为上面的例子中的 XML 文档建立的,特定于数据的对象和 DOM 对象的对象树:

        SalesOrder                  Document
         /      \                       |
       Item    Item                  Element_______
                              ______/ /  \ \______ \_________
                             /       /    \       \          \
                        Element  Element  Element  Element   Element
                           |        |        |    /  |  \ \    etc.
                         Text     Text     Text  /   |   \ \_______
                                                /    |    \        \
                                          Element Element Element  Attr
                                             |       |       |       |
                                           Text    Text    Text    Text
    这个区别在你考虑数据是如何存储在数据库中的时候是非常重要的。要存储特定于数据的对象,你将需要 SalesOrders 和 Items 表;要存储 DOM 对象,你将需要 Document、Element、Text 和 Attr 表。最重要的不同点是非 XML 应用可以使用特定于数据的表,而不能使用特定于 DOM 的表。

    3.1.1. 映射 DTD 到对象模式
    这种映射开始于认识到元素类型是数据类型。有唯 PCDATA 内容的元素类型叫做简单元素类型;术语取自 W3C XML Schema。它们持有一个单一的数据值,并等价于在面向对象编程语言中的标量数据类型。(注意这里用的单词“标量”意思是“由一个单一的数据值组成”。在一些语言中,“标量”数据类型 -- 在这个单词的意义上的 -- 使用对象来表示。最显著的例子是 Java 中的 String 数据类型。) 属性类型也是简单类型。

    有元素或混合内容、或有属性的元素类型叫做复杂数据类型;术语还是取自 XML Schema。它们持有有结构的值,并等价于在面向对象编程语言中的类或 C 中的结构。注意有空内容和有属性的元素类型仍是“复杂的”。如此做的原由是属性也提供了结构并粗略的等价于子唯 PCDATA 元素。

    对象-关系映射首先把简单类型映射成标量数据类型。例如,元素类型 Title 可以映射成 String,而元素类型 Price 可以映射成 float。它接着映射复杂类型到类,同时把这个复杂类型的内容模型中的每个元素类型映射成这个类的类属性。每个类属性的数据类型都是引用的元素类型所映射成的数据类型。例如,到 Title 元素的一个引用将被映射成一个 String 类属性,而到 Price 元素的一个引用将被映射成一个 float 类属性。到复杂元素类型的引用被映射成到复杂元素类型所映射成的类的一个对象的指针/引用。

    映射的最后一部分是把属性映射成类属性,同时由属性的数据类型决定类属性的数据类型。注意这些属性等价于在内容模型中到元素类型的引用。这是因为,就象在内容模型中的引用,它们局限在一个给定的元素类型中。它们只是概念上不同,属性类型是局部定义的,而不是在一个全局(DTD 范围)级别,对于元素类型是这种情况。

    例如,在下面的简单元素类型 B、D、E 和属性 F 都被映射成 String 而复杂元素类型 A 和 C 被映射成类 A 和 C。 A 和 C 的内容模型和属性被映射成类 A 和 C 的类属性。在 A 和 C 的内容模型中到 B、D 和 E 的引用被映射成 String 类属性(因为这些类型被映射成 String),而属性 F 也被映射成一个 String 类属性。在 A 的内容模型中到 C 的引用被映射成带有到类 C 的一个对象的指针/引用的类型的一个类属性,因为元素类型 C 被映射成类 C。

                  DTD                           类
       =========================           =============

       <!ELEMENT A (B, C)>                 class A {
       <!ELEMENT B (#PCDATA)>                 String b;
       <!ATTLIST A                  ==>       C      c;
              F CDATA #REQUIRED>              String f;
                                           }


       <!ELEMENT C (D, E)>                 class C {
       <!ELEMENT D (#PCDATA)>       ==>       String d;
       <!ELEMENT E (#PCDATA)>                 String e;
                                           }
    这里要重申的一个要点是映射在内容模型中的到元素类型的引用不同于映射元素类型自身。元素类型被映射成数据类型,而到元素类型引用被映射成带有有结构的数据类型(类)的类属性。当你考虑到一个元素类型在两个不同的内容模型中被引用的时候这个不同就清楚了。在这种情况下,每个引用必须被单独的映射,同时结果类属性的数据类型由元素类型自身(不是引用)所映射成的数据类型决定。

    例如,考虑下面的 Title 和 Section 元素类型。所有这些元素类型都在 Chapter 和 Appendix 的内容模型中被引用。每个引用都单独为每个父元素类型做映射。把引用到 Title 的类属性的数据类型映射成 String,因为 Title 包含唯 PCDATA 并被映射成一个 String。把引用到 Section 类属性的数据类型映射成到一个 Section 对象的指针/引用,因为 Section 元素类型是复杂的并被映射成一个 Section 类。

                         DTD                                        类
       =========================================               =============

       <!ELEMENT Chapter (Title, Section+)>                    class Chapter {
       <!ELEMENT Appendix (Title, Section+)>          ==>         String    title;
       <!ELEMENT Title (#PCDATA)>                                 Section[] sections;
       <!ELEMENT Section (#PCDATA | p | b | i)*>               }

                                                               class Appendix {
                                                                  String    title;
                                                                  Section[] sections;
                                                               }
    这里要重申的另一个要点是简单元素类型和属性可以映射成除了 String 之外的其他类型。例如,一个叫做 Quantity 的元素类型可以映射成一个整数。在从 DTD 做映射的时候,这需要人为干涉,因为从唯 PCDATA 元素类型无法预知目标数据类型。但是,在从 XML Schema 做映射的时候,因为  XML Schemas 有数据类型所以目标类型是已知的。

    3.1.2. 映射对象模式到数据库模式
    在对象-关系映射的第二部分中,把类映射成表(叫做类表),把标量类属性映射成列,而把指针/引用类属性映射成主键/外键联系,例如:

            类                       表
       ============             =================
       class A {                Table A:
          String b;                Column b
          C      c;     ==>        Column c_fk
          String f;                Column f
       }

       class C {                Table C:
          String d;     ==>        Column d
          String e;                Column e
       }                           Column c_pk
    注意这些表由主键 (C.c_pk) 和外键 (A.c_fk) 连接起来。因为在父元素和子元素之间的联系是一对一的,主键可以在任意一个表中。如果联系是一对多的,主键必须在联系的“一”端,不管是在父元素还是子元素。例如,如果 SalesOrder 元素包含多个 Item 元素,则主键必须在 SalesOrder 表(父元素)中。但是如果每个 Item 元素包含一个 Part 元素,主键必须在 Part 表(子元素)中,因为一个 part 可以出现在多个 item 中。

    可以建立一个主键列作为映射的一部分,列 c_pk 的情况就是这样,或者使用一个现存的列或多个列作为主键。例如,如果一个 SalesOrder 元素类型有一个 Number 子元素,可以把它映射成主键列。

    如果建立一个主键列作为映射的一部分,它的值必须由数据库或传输软件生成。尽管一般认为这是比使用数据列作为主键更好的数据库设计,在与 XML 一起使用的时候这是个缺点,这个生成的键在源数据库之外是无意义的。所以,在带有生成的键的数据传输到 XML 文档中的时候,它要么包含无意义的主键(如果传输了主键)要么根本没有主键(如果没有传输主键)。在后者情况下,有可能不能重标识数据的来源,如果数据被修改并作为 XML 文档被返回到数据库则这就是一个问题。

    3.1.3. 杂记
    在我们继续映射的更复杂部分之前,先需要提及两件事情。首先,在映射期间名字可以改变。例如,DTD、对象模式和关系模式可以全部使用不同的名字。例如,下列的 DTD 使用与下列的类不同的名字:

                    DTD                                   类
       ===============================             =====================

       <!ELEMENT Part (Number, Price)>             class PartClass {    
       <!ELEMENT Number (#PCDATA)>         ==>        String numberProp;
       <!ELEMENT Price (#PCDATA)>                     float  priceProp;
                                                   }
    它使用与下列的表不同的名字:

             类                                表
       =====================             ==================

       class PartClass {                 Table PRT
          String numberProp;     ==>        Column PRTNUM
          float  priceProp;                 Column PRTPRICE
       }
    其次,在映射中涉及到的对象是概念上的。就是说,当在 XML 文档和关系数据库之间传输数据的时候,   不需要实际上的实例化。(这不是说对象不能被实例化。实例化对象是否有用依赖于实际应用。)

    3.2. 映射复杂内容模型
    内容模型迄今为止都是相对简单的。对下面的更复杂的内容模型会怎样?

       <!ELEMENT A (B?, (C | ((D | E | F | G)*, (H | I)+, J?)))>
    在本节中,我们将考虑内容模型的不同部分。上面例子的映射将留给读者做练习。(我总是希望这么说。)

    3.2.1. 映射序列
    象已经见到的那样,把在一个序列中的引用的每个元素类型映射成一个类属性,它将被映射成一个列或者一个主键、外键联系。例如:

                DTD                            类                        表
       ======================              ============             ==============

       <!ELEMENT A (B, C)>                 class A {                Table A
       <!ELEMENT B (#PCDATA)>     ==>         String b;     ==>        Column b
                                              C      c;                Column c_fk
                                           }

       <!ELEMENT C (D, E)>                 class C {                Table C
       <!ELEMENT D (#PCDATA)>     ==>         String d;     ==>        Column d
       <!ELEMENT E (#PCDATA)>                 String e;                Column e
                                           }                           Column c_pk
    3.2.2. 映射选择
    如同序列,把在一个选择中引用的每个元素类型也映射成一个类属性,接着映射成一个列或者一个主键、外键联系。同序列在方式上唯一的不同是这种类属性和列可以是 null。例如,假设我们在把在 A 的内容模型中的序列改成一个选择。从 DTD 到对象模式的映射将是

                DTD                                类
       ======================              ========================

       <!ELEMENT A (B | C)>                class A {
       <!ELEMENT B (#PCDATA)>     ==>         String b; // Nullable
                                              C      c; // Nullable
                                           }
       <!ELEMENT C (D, E)>                 class C {
       <!ELEMENT D (#PCDATA)>     ==>         String d;
       <!ELEMENT E (#PCDATA)>                 String e;
                                           }
    而从对象模式到数据库模式的映射将是

               类                                    表
       ========================             ===========================

       class A {                            Table A (
          String b; // Nullable     ==>        Column b     // Nullable
          C      c; // Nullable                Column c_fk  // Nullable
       }
       class C {                            Table C (
          String d;                 ==>        Column d     // Not nullable
          String e;                            Column e     // Not nullable
       }                                       Column c_pk  // Not nullable
    要知道为什么是这样,考虑下列 XML 文档,它遵守的上面的 DTD。因为在 A 的内容模型中的选择要求要么 B 要么 C (但不能两个都有)作为子元素而存在,两个相应的类属性(和列)中的一个将总是 null。

           XML                         对象                       表
       =============               ============               ===========

                                                                Table A
                                                               ---------
       <A>                         object a {                  b    c_fk
          <B>bbb</B>      ==>         b = "bbb"      ==>      ---   ----
       </A>                           c = null                bbb   NULL
                                   }                          ...   ...
    注意如果用来连接表的这个主键在表 A 中,则在表 C 中对应的外键列不能是有空值的。如果 A 元素确实有一个 C 子元素,这个列必须有一个值来把它连接到表 A 中正确的行。如果 A 元素确实没有 C 子元素,则简单的在表 C 中没有行。

    3.2.3. 映射重复子元素
    子元素在它们的父元素中可以出现多次,就叫做重复子元素,并被映射成多值类属性并接着映射成在一个表中的多个列或到一个单独的表,叫做属性表。

    如果一个内容模型包含到一个简单类型的重复的引用,这些引用被映射成一个单一的类属性,它是已知大小的一个数组。它可以被映射成在一个表中的多个列或一个属性表。例如,下面展示如何把一个重复的引用映射成一个表中的多个列:

                  DTD                                 类                           表
       =========================                 ==============               ===========

       <!ELEMENT A (B, B, B, C)>                 class A {                    Table A
       <!ELEMENT B (#PCDATA)>         ==>           String[] b;      ==>         Column b1
       <!ELEMENT C (#PCDATA)>                       String   c;                  Column b2
                                                 }                               Column b3
                                                                                 Column c
    如果对一个引用提供了 + 或 * 操作符,则再次把引用映射成一个单一的类属性,它这次是未知大小的一个数组。因为值的数目可以是任意大的,这种类属性必须被映射成一个属性表,它将为每个值包含一行。通过主键、外键联系把属性表连接到类表,这里的主键在类表中。例如:

                DTD                              类                          表
       ======================               ==============               ==============

       <!ELEMENT A (B+, C)>                 class A {                    Table A
       <!ELEMENT B (#PCDATA)>      ==>         String[] b;      ==>         Column a_pk
       <!ELEMENT C (#PCDATA)>                  String   c;                  Column c
                                            }
                                                                         Table B
                                                                            Column a_fk
                                                                            Column b
    3.2.4. 映射可选子元素
    把可选的子元素映射成有空值的类属性,接着映射成有空值的列。对于出现在选择组中的子元素这已经见到过了,比如下面的从 DTD 到对象模式的映射:

                  DTD                               类
       =========================            ========================

       <!ELEMENT A (B | C | D)>             class A {
       <!ELEMENT B (#PCDATA)>       ==>        String b; // Nullable
       <!ELEMENT C (#PCDATA)>                  String c; // Nullable
                                               D      d; // Nullable
                                            }
       <!ELEMENT D (E, F)>                  class D {
       <!ELEMENT E (#PCDATA)>       ==>        String e;
       <!ELEMENT F (#PCDATA)>                  String f;
                                            }
    和对象模式到数据库模式的映射:

                类                                   表
       ========================               ========================

       class A {                              Table A
          String b; // Nullable      ==>         Column b     // Nullable
          String c; // Nullable                  Column c     // Nullable
          D      d; // Nullable                  Column d_fk  // Nullable
       }
       class D {                              Table D
          String e;                  ==>         Column e     // Not nullable
          String f;                              Column f     // Not nullable
       }                                         Column d_pk  // Not nullable
    在对引用应用了 ? 或 * 操作符的时候它也是适用的,比如下面的从 DTD 到对象模式的映射:

                DTD                                  类
       ======================               ==========================

       <!ELEMENT A (B?, C*)>                class A {
       <!ELEMENT B (#PCDATA)>      ==>         String b;   // Nullable
       <!ELEMENT C (#PCDATA)>                  String c[]; // Nullable
                                            }
    和对象模式到数据库模式的映射:

                类                                       表
       ==========================               ===============================

       class A {                                Table A
          String b;   // Nullable      ==>         Column b     // Nullable
          String c[]; // Nullable                  Column a_pk  // Not nullable
       }
                                                Table C
                                                   Column a_fk  // Not nullable
                                                   Column c     // Not nullable
    注意用来存储  C 的列(在属性表 C 中)不能是有空值的。这是因为,如果在 A 元素中不存在 C 子元素 A,则简单的在表 C 中没有行。

    3.2.5. 映射子组
    把在子组中的引用映射成父类的类属性,接着映射成在类表中的列,比如下面的从 DTD 到对象模式的映射:

                DTD                                     类
       =========================              ============================

       <!ELEMENT A (B, (C | D))>              class A {
       <!ELEMENT B (#PCDATA)>        ==>         String b; // Not nullable
       <!ELEMENT C (#PCDATA)>                    String c; // Nullable
       <!ELEMENT D (E, F)>                       D      d; // Nullable
                                              }
       <!ELEMENT D (E, F)>                    class D {
       <!ELEMENT E (#PCDATA)>        ==>         String e;
       <!ELEMENT F (#PCDATA)>                    String f;
                                              }
    和对象模式到数据库模式的映射:

               类                                       表
       ============================               ==========================

       class A {                                  Table A
          String b; // Not nullable      ==>         Column b    // Not nullable
          String c; // Nullable                      Column c    // Nullable
          D      d; // Nullable                      Column d_fk // Nullable
       }
       class D {                                  Table D (
          String e;                       ==>        Column e
          String f;                                  Column f
       }                                             Column d_pk
    你可能在想象这是如何可能的。对于子组所强加的结构发生了什么? 实际上,这种结构只出现在内容模型中,而不在实例文档中。例如,下列两个文档都满足上面的内容模型:

       <A>
          <B>bbbbbb</B>
          <C>cccccc</C>
       </A>

       <A>
          <B>bbbbbb</B>
          <D>
             <E>eee</E>
             <F>fff</F>
          </D>
       </A>
    从文档中无法确定子组的存在。在结构上,C 和 D 不能区别于 B;它们都只是 A 的子元素。所以,它们可以用象 A 的不在子组中的子元素那样映射。

    把在子组中的引用直接映射成在父类中的类属性的一个结果是重复性和可选性可以是间接的。例如,在下列内容模型中 C、D 和 E 是既可选的又可重复的。它们是可重复的、因为 + 操作符间接的应用到它们上。C 是可选择的、因为它间接的在一个选择组中,而 D 和 E 是可选择的、因为它们间接的在一个选择组中。

                   DTD                                       类
       ===============================            =============================

       <!ELEMENT A (B, (C | (D, E))+)>            class A {
       <!ELEMENT B (#PCDATA)>                        String   b;
       <!ELEMENT C (#PCDATA)>            ==>         String[] c; // May be null  
       <!ELEMENT D (E, F)>                           D[]      d; // May be null
       <!ELEMENT E (#PCDATA)>                        String[] e; // May be null
                                                  }
    3.3. 映射混合内容
    除了可以包含混合在子元素之间的 PCDATA 之外,混合内容只是 * 操作符间接的应用在其上的一个选择组。所以,可以把混合内容中的元素类型引用首先映射成未知大小的有空值的数组类属性,接着映射成属性表。

    要看如何映射混合的内容,考虑下列 XML 文档:

       <A>
       This text <c>cc</c> makes
       <b>bbbb</b> no sense
       <c>cccc</c> except as
       <b>bb</b> an example.
       </A>
    然后注意它在本质上同于下列文档,其中的 PCDATA 被包装载 <pcdata> 元素中:

       <A>
       <pcdata>This text </pcdata><c>cc</c><pcdata> makes
       </pcdata><b>bbbb</b><pcdata> no sense
       </pcdata><c>cccc</c><pcdata> except as
       </pcdata><b>bb</b><pcdata> an example.</pcdata>
       </A>
    从此,容易看出 PCDATA 可以象其他子元素一样对待。所以,把在混合内容中的 PCDATA 映射成已知道小的有空值的一个数组,接着映射成一个属性表。下面展示如何把混合内容从 DTD 映射到对象模式:

                   DTD                                类
       ===============================           ===================

                                                 class A {
       <!ELEMENT A (#PCDATA | B | C)*>              String[] pcdata;
       <!ELEMENT B (#PCDATA)>            ==>        String[] b;
       <!ELEMENT C (#PCDATA)>                       String[] c;
                                                 }
    并从对象模式映射成数据库模式:

             类                                 表
       ===================              ===================================

                                                           Table PCDATA
                                                        ------Column a_fk
       class A {                                       /      Column pcdata
          String[] pcdata;              Table A       /    Table B
          String[] b;           ==>        Column a_pk--------Column a_fk
          String[] c;                                 \       Column b
       }                                               \   Table C
                                                        \-----Column a_fk
                                                              Column c
    要看在数据库中实际存储的东西,考虑在本节开始处展示的文档,它被映射成下面的对象,接着映射成灾下列的表中的行。(我们假定为在给 A 的表中的行生成值为 1 的一个主键。用它来把在表 A 中的行连接到其他表上。)

                对象                                        表
       ============================               ===============================

                                                                Table PCDATA
                                                                a_fk  pcdata
                                                                ----  -----------
                                                                 1    This text
                                                                 1    makes
       object a {                                                1    no sense
          pcdata = {"This text ",                                1    except as
                    " makes ",                    Table A        1    an example.
                    " no sense ",                   a_pk       
                    " except as",        ==>        ----        Table B
                    " an example."}                  1          a_fk   b
          b      = {"bbbb", "bb"}                               ----  ----
          c      = {"cc", "cccc"}                                1    bbbb
       }                                                         1    bb

                                                                Table C
                                                                a_fk   c
                                                                ----  ----
                                                                 1    cc
                                                                 1    cccc
    从这个例子中明显容易的看出的一件事情是对象-关系映射在存储混合内容上不是很有效率的。为此,它更通常使用在以数据为中心的应用中,它们倾向于有很少的混合内容。

    有两种方式解决这个问题,第一个是使用不同于对象-关系映射的其他映射。例如,如果这个文档使用 DOM 或类似结构来建模,并使用对象-关系映射把它映射到数据库,数据库中只有很少的表 -- Document, Element, Attr, Text 等。-- 尽管需要一个类似的连接编号来检索一个文档。第二种策略是不是把文档分解成最小的可能构件,而是分解成大的片断,比如章或节。这种策略可以与对象-关系映射一起使用;详情请参见章节 3.6.1,“映射复杂元素类型到标量类型”。

    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 &lt;b&gt; 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)


    [此贴子已经被admin于2004-2-24 0:38:42编辑过]

       收藏   分享  
    顶(0)
      




    点击查看用户来源及管理<br>发贴IP:*.*.*.* 2003/12/22 12:26:00
     
     GoogleAdSense
      
      
      等级:大一新生
      文章:1
      积分:50
      门派:无门无派
      院校:未填写
      注册:2007-01-01
    给Google AdSense发送一个短消息 把Google AdSense加入好友 查看Google AdSense的个人资料 搜索Google AdSense在『 XML 与 数据库 』的所有贴子 访问Google AdSense的主页 引用回复这个贴子 回复这个贴子 查看Google AdSense的博客广告
    2024/4/27 11:07:55

    本主题贴数8,分页: [1]

     *树形目录 (最近20个回帖) 顶端 
    主题:  映射 DTD 到数据库(63254字) - mhss,2003年12月22日
        回复:  好人啊!正需要这方面的资料,哈哈!(34字) - kittypig,2004年7月13日
        回复:  3Q(2字) - 凤舞九天,2004年7月8日
        回复:  thx very much(13字) - nolf,2004年5月13日
        回复:  3x(2字) - nizijian,2004年4月12日
        回复:  xiexie(6字) - intopython,2004年1月16日
        回复:  thank you !(11字) - szn,2003年12月27日
        回复:  存储 XML 到关系数据库中:(20055字) - mhss,2003年12月26日

    W3C Contributing Supporter! W 3 C h i n a ( since 2003 ) 旗 下 站 点
    苏ICP备05006046号《全国人大常委会关于维护互联网安全的决定》《计算机信息网络国际联网安全保护管理办法》
    453.125ms