5.8 一致性约束

定义元素除了可以使用上面所介绍的各种语法之外,还可以指定3种类型的约束:

alt key约束:相当于数据库里的主键约束,要求指定内容必须存在而且唯一。

alt keyref约束:相当于数据库里的外键约束,要求指定内容的值必须引用另一个key约束或unique约束。keyref约束用于建立两个数据项之间的引用关系。

alt unique约束:相当于数据库里的唯一约束,要求指定内容必须唯一,但可以不存在。

这3个一致性约束都只能在<element…/>元素内定义,且只能在<element…/>元素的最后面定义。

在数据库中定义约束时,不仅需要指定使用哪类约束,还需要定义应该对哪些字段应用约束。在Schema中定义一致性约束也完全相似,也需要指定该约束将对哪些部分起作用,因此需要在约束内使用如下两个子元素:

alt <selector…/>:该元素需要指定一个xpath属性,其值是一个XPath表达式,用来确定一个元素范围。在一次约束定义中,<selector…/>元素必须出现一次,也只能出现一次。

alt <field…/>:该元素需要指定一个xpath属性,其值也是一个XPath表达式。在一次约束定义中,<field…/>元素至少要出现一次,也可以出现多次。

对于任何约束而言,<selector…/>和<field…/>元素用于指定约束的作用部分,其含义是:在<selector…/>元素的XPath表达式所表示的范围内,<field…/>元素的XPath表达式所表示的内容必须遵守一致性约束。如果同时指定了多个<field…/>子元素,则它们的XPath表达式所表示内容的组合必须遵守一致性约束,但单个XPath表达式所表示的内容无须遵守一致性约束(有点类似于数据库里的多列组合约束)。


alt提示

可能有读者对XPath感到比较困惑,认为它又是一种很高深的知识。其实XPath的作用非常简单:用于导航、确定XML文档的元素。例如如下XML文档:

alt

其中有两个<c…/>元素,很明显它们不是同一个元素,但它们的标签名都是“c”,显然仅凭标签名无法唯一地确定一个元素。

通过前面的介绍我们知道:XML文档可以转换为结构树。因此上面的结构化文档可转换为图5.3所示结构化树。

alt

图5.3 结构化树

仔细查看图5.3所示的结构树,不难发现它和Windows资源管理器树非常相似,图5.4所示是笔者计算机里G盘的资源管理器树。

alt

图5.4 资源管理器树

对于图5.4所示资源管理器树,如果需要确定名为5.7的节点,读者都知道可以使用G:\publish\codes\05\5.7——这是一条最常用的绝对路径。XPath也是这种思想:例如想访问内容是“孙悟空”的节点,则应该使用/a/b/c——这就是XPath表达式;如果想访问内容是“白骨精”的节点,则应该使用/a/c。如果直接给出c,它也是XPath表达式,但它既可以匹配内容是“孙悟空”的节点,也可以匹配内容是“白骨精”的节点。

读者现在只需了解XPath的基本思想即可,本书后面还有关于XPath更深入、更细致的介绍。


5.8.1 key约束

key约束要求被约束的内容必须存在,而且必须唯一。如下Schema是使用key约束的简单示例:

程序清单:codes\05\5.8\key.xsd

alt

上面的①号代码表明在<book-list…/>元素之内定义key约束,这表明该约束仅在该元素内有效,在<book-list…/>元素之外,该key约束将不会有任何效果。

上面的②、③两行代码所指定的XPath表达式都没有以斜线(/)开头,这表明它们都是相对路径,总是以当前上下文作为基础路径。②号代码位于<book…/>元素之内,点号(.)代表当前路径,也就是说在<book…/>元素之内。

在<selector…/>元素中XPath表达式所表示的范围之内,<field…/>元素中XPath表达式所表示的内容必须满足key约束,也就是说,在<book…/>节点之内<name…/>节点的内容必须存在且唯一。

对于上面的Schema,如下XML文档是有效的:

程序清单:codes\05\5.8\key.xml

alt

如果将上面的XML文档中两个<book…/>元素里<name…/>元素的值改为相同值,则XMLSpy中将出现图5.5所示的错误提示。

alt

图5.5 违反key约束的提示信息

如果在指定约束内使用了多个<field…/>子元素,则只要求多个<field…/>子元素所表示内容的组合遵守该约束。如以下Schema所示:

程序清单:codes\05\5.8\key2.xsd

alt

上面的Schema中在定义key约束时指定了两个<field…/>子元素,表明只要两个<field…/>子元素所表示的内容(name子元素、publish-date属性)满足key约束即可。如下XML文档是有效的:

程序清单:codes\05\5.8\key2.xml

alt

5.8.2 unique约束

unique约束和key约束的用法基本相同,只是它不要求被约束内容必须存在,因此可以认为它比key约束要求略松。如下Schema示范了如何使用unique约束:

程序清单:codes\05\5.8\unique.xsd

alt

对于上面的Schema,如下XML文档是有效的:

程序清单:codes\05\5.8\unique.xml

alt

值得指出的是,虽然unique约束允许被约束内容不存在,实质上是空字符,但最多只能有一个被约束的内容为空。

5.8.3 keyref约束

keyref约束有点类似于数据库里的外键约束,因为keyref约束要求被约束内容引用key约束或unique约束的内容。

假设需要定义系列图书,各图书之间有先后关系(例如阅读《疯狂Ajax讲义》之前,应先阅读《疯狂Java讲义》),我们可以为<book…/>元素增加一个<pre-book…/>元素,它的值引自另一个<book…/>元素里<name…/>元素的值,它的pre-isbn属性引自另一个<book…/>元素的isbn属性——这样就建立了两本图书之间的先后关系。

在数据库中定义外键约束时,必须指定外键需要引用哪个主键或唯一键,在Schema中keyref约束也需要指定该约束要引用哪个key约束或unique约束,因此定义<keyref…/>元素时可指定refer属性,其值用来指定该约束将引用哪个key约束或unique约束。

如下Schema示范了如何使用keyref约束:

程序清单:codes\05\5.8\keyref.xsd

alt

对于上面的Schema,如下XML文档是有效的:

程序清单:codes\05\5.8\keyref.xml

alt