映射 DTD 到数据库[上]

类别:.NET开发 点击:0 评论:0 推荐:
原著作者: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,“映射复杂元素类型到标量类型”。

本文地址:http://com.8s8s.com/it/it42936.htm