XML/一对一关系

< XML

XML > 一对一关系


学习目标

完成本章内容的学习您将能够:

  • 为含有一对一关系的数据模型创建schema
  • 在XML schema中为元素或属性添加约束
  • 在XML schema中定义元素的默认值
  • 在XML schema中使用次序标识来定义在XML文档中元素的排列次序
  • 在XML样式表中使用排序元素来对于输出结果进行排序
  • 在XML样式表中使用if语句
  • 编写一段将XML文档内容插入关系数据库的java程序

概述 编辑

在上一章中,我们首先介绍了XML schema、XML文档和样式表的一些新的特点,同时还给出了为一对多关系进行建模的方法。在本章中,我们将对XML schema和XML样式表的特点作更进一步的介绍。同时我们也将介绍XML中的一对一关系的建模,另外,我们将讨论如何编写将XML文档内容插入关系数据库的java程序。

一对一关系 编辑

 
图4-1 一对一数据模型

XML schema 编辑

图4-1中的数据模型表示了一个一对一(1:1)关系。添加了country和destination后便出现了一个一对一关系topDestination。

每一个country拥有许多不同的destination,但是只有一个top destination。表4-1中的XML schema给出了如何在XML schema中定义一个一对一关系的实例。


<?xml version="1.0" encoding="UTF-8"?>

<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" elementFormDefault="unqualified">

<xsd:element name="tourGuide">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="city" type="cityDetails" minOccurs="1" maxOccurs="unbounded" />
</xsd:sequence>
</xsd:complexType>
</xsd:element>


<xsd:complexType name="cityDetails">
<xsd:sequence>
<xsd:element name="cityName" type="xsd:string"/>
<xsd:element name="adminUnit" type="xsd:string"/>
<xsd:element name="country" type="countryDetails" minOccurs="1" maxOccurs="1"/>
<xsd:element name="population" type="xsd:integer" default="0"/>
<xsd:element name="area" type="xsd:integer"/>
<xsd:element name="elevation" type="xsd:integer"/>
<xsd:element name="longitude" type="xsd:decimal"/>
<xsd:element name="latitude" type="xsd:decimal"/>
<xsd:element name="description" type="xsd:string"/>
<xsd:element name="history" type="xsd:string"/>
<xsd:element name="hotel" type="hotelDetails" minOccurs="0" maxOccurs="unbounded" />
</xsd:sequence>
</xsd:complexType>


<xsd:simpleType name="emailAddressType">
<xsd:restriction base="xsd:string">
<xsd:pattern value="\w+\W*\w*@{1}\w+\W*\w+.\w+.*\w*"/>
</xsd:restriction>
</xsd:simpleType>
<xsd:complexType name="hotelDetails">
<xsd:sequence>
<xsd:element name="hotelPicture"/>
<xsd:element name="hotelName" type="xsd:string"/>
<xsd:element name="streetAddress" type="xsd:string"/>
<xsd:element name="postalCode" type="xsd:string" minOccurs="0" />
<xsd:element name="telephoneNumber" type="xsd:string"/>
<xsd:element name="emailAddress" type="emailAddressType" minOccurs="0" />
<xsd:element name="websiteURL" type="xsd:any" minOccurs="0" />
<xsd:element name="hotelRating" type="xsd:integer" default="0"/>
</xsd:sequence>
</xsd:complexType>


<xsd:complexType name="countryDetails">
<xsd:sequence>
<xsd:element name="countryName" type="xsd:string" minOccurs="1" maxOccurs="1"/>
<xsd:element name="population" type="xsd:integer" minOccurs="0" maxOccurs="1" default="0"/>
<xsd:element name="continent" minOccurs="0" maxOccurs="1">
<xsd:simpleType>
<xsd:restriction base="xsd:string">
<xsd:enumeration value="Asia"/>
<xsd:enumeration value="Africa"/>
<xsd:enumeration value="Australasia"/>
<xsd:enumeration value="Europe"/>
<xsd:enumeration value="North America"/>
<xsd:enumeration value="South America"/>
<xsd:enumeration value="Antarctica"/>
</xsd:restriction>
</xsd:simpleType>
</xsd:element>
<xsd:element name="topDestination" type="destinationDetails" minOccurs="0" maxOccurs="1"/>
<xsd:element name="destination" type="destinationDetails" minOccurs="0" maxOccurs="unbounded"/>
</xsd:sequence>
</xsd:complexType>


<xsd:complexType name="destinationDetails">
<xsd:all>
<xsd:element name="destinationName" type="xsd:string"/>
<xsd:element name="description" type="xsd:string"/>
<xsd:element name="streetAddress" type="xsd:string" minOccurs="0"/>
<xsd:element name="telephoneNumber" type="xsd:string" minOccurs="0"/>
<xsd:element name="websiteURL" type="xsd:any" minOccurs="0"/>
</xsd:all>
</xsd:complexType>

</xsd:schema>


表 4-1: 一对一关系的XML schema – country_dest.xsd

下面我们检视一下表4-1中出现的一些新元素和新属性。

  • 元素Country是定义在元素City中的一个复杂类型,其作用是表示国家和其城市的一对多(1:M)关系。
  • 元素Destination是定义在元素Country中的一个复杂类型,其作用是表示一个国家和其许多目的地之间的一对多关系。
  • 元素topDestination是一个定义在元素Country中的复杂类型,其作用是表示国家和其首要目的地之间的一对一关系。

:上章中已经介绍了为属性添加约束的方法,但是对元素而言还有更多潜在的有用约束。例如可以通过在元素或属性上面添加 某种约束从而影响处理器处理空格字符的方式方法:


<xsd:element name="address">

<xsd:simpleType>
<xsd:restriction base="xsd:string">
<xsd:whiteSpace value="preserve"/>
</xsd:restriction>
</xsd:simpleType>

</xsd:element>

约束whiteSpace被定义为“preserve”,这就意味着XML处理器将不会移除任何空格字符。其他有用的约束还包括:

  • Replace – XML处理器将所有的空格字符用空格来替代:
<xsd:whiteSpace value="replace"/>
  • Collapse – 处理器将移除所有的空格字符:
<xsd:whiteSpace value="collapse"/>
  • Length, maxLength, minLength—元素能够确定的长度或允许的长度范围:
<xsd:length value="8"/>
<xsd:minLength value="5"/>
<xsd:maxLength value="8"/>
除了能够对于元素添加约束之外,顺序标识可以用来定义元素出现的次序。标识<all>默认规定:子元素可以以任何次序出

现并且每个子元素仅允许出现一次:


<xsd:element name="person">

<xsd:complexType>
<xsd:all>
<xsd:element name="firstname“ type="xsd:string"/>
<xsd:element name="lastname" type="xsd:string"/>
</xsd:all>
</xsd:complexType>

</xsd:element>

标识<choice>规定在指定的两个子元素中只有一个能够出现:


<xsd:element name="person">

<xsd:complexType>
<xsd:choice>
<xsd:element name="employee" type="employee"/>
<xsd:element name=“visitor" type=“visitor"/>
</xsd:choice>
</xsd:complexType>

</xsd:element>

标识<sequence>规定子元素必须以一个特定的顺序出现:


<xsd:element name="person">

<xsd:complexType>
<xsd:sequence>
<xsd:element name="firstname" type="xsd:string"/>
<xsd:element name="lastname" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>

</xsd:element>

XML文档 编辑

表4-2中的XML文档显示了如何在XML文档中应用表4-1中XML schema定义的新元素(country和destination)。注意,

子元素<topDestination>可以按任何次序出现,因为在schema中定义了次序标识<xsd:all>。


<?xml version="1.0" encoding="UTF-8"?>

<?xml-stylesheet type="text/xsl" href="country.xsl" media="screen"?>
<tourGuide xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xsi:noNamespaceSchemaLocation='file:/C:/XML/country.xsd'>

<city>
<cityName>Kuala Lumpur</cityName>
<adminUnit>Selangor</adminUnit>
<country>
<countryName>Malaysia</countryName>
<population>22229040</population>
<continent>Asia</continent>
<topDestination>
<description>A popular duty-free island north of Penang.</description>
<destinationName>Pulau Langkawi</destinationName>
</topDestination>
<destination>
<destinationName>Muzium Di-Raja</destinationName>
<description>The original palace of the Sultan</description>
</destination>
<destination>
<destinationName>Kinabalu National Park</destinationName>
<description>A national park</description>
</destination>
</country>
<population>1448600</population>
<area>243</area>
<elevation>111</elevation>
<longitude>101.71</longitude>
<latitude>3.16</latitude>
<description>Kuala Lumpur is the capital of Malaysia and is the largest city in the nation.</description>
<history>The city was founded in 1857 by Chinese tin miners and superseded Klang. In 1880 the British government transferred their headquarters from Klang to Kuala Lumpur, and in 1896 it became the capital of Malaysia.</history>
</city>
<city>
<cityName>Belmopan</cityName>
<adminUnit>Cayo</adminUnit>
<country>
<countryName>Belize</countryName>
<population>249183</population>
<continent>South America</continent>
<topDestination>
<destinationName>San Pedro</destinationName>
<description>San Pedro is an island off the coast of Belize</description>
</topDestination>
<destination>
<destinationName>Belize City</destinationName>
<description>Belize City is the former capital of Belize</description>
</destination>
<destination>
<destinationName>Xunantunich</destinationName>
<description>Mayan ruins</description>
</destination>
</country>
<population>11100</population>
<area>5</area>
<elevation>130</elevation>
<longitude>12.3</longitude>
<latitude>123.4</latitude>
<description>Belmopan is the capital of Belize</description>
<history>Belmopan was established following devastation of the former capitol, Belize City, by Hurricane Hattie in 1965. High ground and open space influenced the choice and ground-breaking began in 1966. By 1970 most government offices and operations had already moved to the new location.</history>
<hotel>
<hotelPicture filename="bull_frog_inn.jpg" size="80" value="Image of Bull Frog Inn" imageURL="http://www.bullfroginn.com"/>
<hotelName>Bull Frog Inn</hotelName>
<streetAddress>25 Half Moon Avenue</streetAddress>
<telephoneNumber>501-822-3425</telephoneNumber>
<emailAddress>bullfrog@btl.net</emailAddress>
<websiteURL>http://www.bullfroginn.com/</websiteURL>
<hotelRating>4</hotelRating>
</hotel>
</city>

</tourGuide>


表 4-2:一对一关系的XML文档 – country_dest.xml

XML样式表 编辑

<?xml version="1.0" encoding="UTF-8" ?>

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html"/>
<xsl:template match="/">

<HTML>
<HEAD>
<TITLE>Tour Guide</TITLE>
<STYLE TYPE="text/css">
H2 {TEXT-ALIGN:CENTER;}
.greenBackground {BACKGROUND-COLOR:LIGHTGREEN; TEXT-ALIGN:CENTER;}
.yellowBackground {BACKGROUND-COLOR:YELLOW; TEXT-ALIGN:CENTER; FONT-WEIGHT:BOLD; FONT-SIZE:14pt;}
.salmonBackground {BACKGROUND-COLOR:LIGHTSALMON; TEXT-ALIGN:CENTER; FONT-SIZE:12pt;}
</STYLE>
</HEAD>
<BODY>
<H2>Top Tourist Destinations</H2>
<xsl:apply-templates select="tourGuide"/>
</BODY>
</HTML>

</xsl:template>
<xsl:template match="tourGuide">

<TABLE BORDER="1" WIDTH="100%">
<xsl:for-each select="city/country">
<xsl:sort select="countryName"/>
<xsl:if test="population &gt; 10000">
|-----
| CLASS="greenBackground" | <BR/>
<xsl:text>Country: </xsl:text><xsl:value-of select="countryName"/> <BR/>
| CLASS="greenBackground" | <BR/>
<xsl:text>Population: </xsl:text><xsl:value-of select="population"/>


<xsl:for-each select="topDestination">
| CLASS="yellowBackground" | <BR/>
<xsl:text>Top Destination: </xsl:text><xsl:value-of select="destinationName"/>


</xsl:for-each>
<xsl:for-each select="destination">
| CLASS="yellowBackground" | <BR/>
<xsl:text>Destination: </xsl:text><xsl:value-of select="destinationName"/>

</xsl:for-each>

</xsl:if>
</xsl:for-each>
</TABLE>
</xsl:template>

</xsl:stylesheet>


表 4-3: 一对一关系XML样式表– country_dest.xsl

表4-3中的样式表介绍了if语句,以及排序元素。
if语句代码的执行与否取决于对于“test”属性中所描述条件的评估。例如

<xsl:if test="population &gt;= 10000">

Big Town

</xsl:if>
<xsl:if test="population &lt; 10000">

Small Town

</xsl:if>

运算符的含义

运算符

含义

= 两个取值相等
!= 两个取值不相等
&lt; 小于
&gt; 大于
&lt;= 小于等于
&gt;= 大于等于
and 逻辑与
or 逻辑或




元素<xsl:sort>指定属性的取值可以规定输出结果的排序。

利用Java执行SQL插入语句 编辑

步骤 1: 为了能够在Java程序中执行SQL语句,我们必须首先建立与数据库的连接。为了建立连接首先要建立一个驱动的实 例。例如,如果您想要连接到一个PostgreSQL数据库,以下的一行代码便是用来建立驱动实例的:

Class.forName("org.postgresql.jdbc.Driver").newInstance();

步骤2: 一旦驱动实例建立成功,那么只要知道数据库的url并且拥有有效的用户名和密码,你便可以建立起连接。以下的两 行代码示范了如何建立连接:

String url = "jdbc:postgresql://localhost:5432/dbName";
Connection con = DriverManager.getConnection(url,"用户名","密码");

步骤3: 一旦连接成功建立,便必须创建一个声明。声明可以利用createStatement() 函数进行创建:

Statement stmt = con.createStatement();

步骤 4: 当您完成上述步骤后,便可以利用java.sql.Statement.execute()方法执行SQL语句。例如:

stmt.execute(“SELECT * FROM city”);

步骤 5: 当执行完SQL语句后,不要忘记使用Statement.close() 或 Connection.close()方法关闭与数据库的连接。

下面的Java程序给出了如何利用Java执行SQL语句的实例。openConnection() 方法建立了与一个特定数据库的连接并且

创建了一个Statement对象。closeConnection() 方法结束语句执行并关闭与数据库的连接。insertStatements() 方 法创建插入语句并执行之。


import javax.xml.parsers.*;
import org.xml.sax.*;
import org.w3c.dom.*;
import java.io.*;
import java.util.Vector;
import java.sql.*;

public class MySimpleDOMParser
{

// global reference to our DOM object
static Document doc;
private String url;
private Statement stmt;
private Connection con;


// parse given XML file when creating MyParser object
public MySimpleDOMParser(String filename)
{
// create file object that holds the XML file
File file = new File(filename);


try
{
/* THREE steps are needed to create a parser and save the document data into a DOM object
1. create factory object providing the parser */
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();


// 2. create parser object building the DOM object
DocumentBuilder parser = factory.newDocumentBuilder();


// 3. use parse method to create DOM object from the given file
// store the resulting DOM object into the "doc" variable
doc = parser.parse(file);


// +++ NOW YOU CAN CALL ANY METHOD TO WORK WITH THIS DATA
// example - printNodes: prints out all nodes in the DOM tree
// insertStatements: creates strings holding the insert statement


// If the document isn't well-formed, an exception has
// already been thrown and this has been skipped.
System.out.println(file + " is well-formed.");


/* The parsing will only succeed if the XML file was well-formed, in any
* other case it will throw an exception that is caught by the
* following catch blocks */
} catch (SAXException e)
{
System.out.println(file + " is not well-formed.");
} catch (IOException e)
{
// usually happens if the file was not found
System.out.println("Due to an IOException, the parser could not check " + file);
} catch (FactoryConfigurationError e)
{
System.out.println("Could not locate a factory class");
} catch (ParserConfigurationException e)
{
// the JAXP class library was not loaded correctly
System.out.println("Could not locate a JAXP parser");
}
}


// create, display, and execute insert statements derived from the DOM object
public void insertStatements(Node n)
{
openConnection();
String sqlStmt;


// get the node name == table name
String tablename = n.getNodeName();
System.out.println("Creating insert statements for table : " + tablename);


/* get a list of this nodes children (= holding the values to be inserted into table) */
NodeList values = n.getChildNodes();


// save the information about the values that are to be inserted in a cache
Vector cache = new Vector();


// for every child node do the same - recursive call
for (int i = 0; i < values.getLength(); i++)
{
/* browse through the children of the given node (e.g. cityName, adminUnit...) */
Node child = values.item(i);


// we're not interested in whitespace-text nodes, so we skip them
if (child.getNodeType() != Node.TEXT_NODE)
{
// get the text node holding the textual information about the parent node
// e.g. "cityName" has a child text node holding the city's name
String insert = child.getFirstChild().getNodeValue();


// once again: skip whitespace nodes
if (insert.trim().length() != 0)
{
insert = "\""+insert+"\"";
cache.add(insert);
}
}
}
sqlStmt = "INSERT INTO " + tablename + " VALUES (";
int index = 0;
while (index < cache.size()-1)
{
sqlStmt += cache.get(index) + ", ";
index++;
}
sqlStmt += cache.get(index);
System.out.print(sqlStmt);
System.out.println(")");
try
{
stmt.execute(sqlStmt);
}catch(Exception e)
{
System.err.println(e);
}
closeConnection();
}


public void openConnection()
{
try
{
Class.forName("org.postgresql.jdbc.Driver").newInstance();
url = "jdbc:postgresql://www.opentourism.org:5432/text";
con = DriverManager.getConnection(url,"userName","pwd");
stmt = con.createStatement();
}catch(Exception e)
{
System.err.println(e);
}
}


public void closeConnection()
{
try
{
stmt.close();
con.close();
}catch(Exception e)
{
e.printStackTrace();
}
}


public static void main(String[] args)
{
// call this application with the command line arguments "file.xml file.xsd"
MySimpleDOMParser p = new MySimpleDOMParser(args[0]);


// get a list of all element nodes "city"
NodeList n = doc.getElementsByTagName("city");


// for each "city" in this node list do the following:
for (int i=0; i<n.getLength(); i++)
{
/* call the insertStatements method (which will print out the statements and execute them) */
p.insertStatements(n.item(i));
}
}

}



有关java更详细的信息请参看 Java API.
PostgreSQL JDBC 连接驱动可以在如下地址下载PostgreSQL JDBC website.

总结 编辑

Schema设计者可以添加对于元素长度的约束同时定义处理器对于空格字符的处理策略。Schema设计者也可以指定元素的默

认值。次序标识可以用来指定XML文档中元素的出现顺序。

XML样式表中排序元素可以用来根据特定的元素顺序对输出结果进行排序。另外“if statements”可以用来输出满足某一

测试条件的元素。

Java程序能够用来解析XML文档,生成并执行SQL插入语句集。


练习 编辑

1.建立一个用来描述一个城市中最受欢迎的餐厅的XML schema。在schema中使用all标识或者choice标识。并检验其良构 性和有效性。
2.使用schema创建一个XML文档,并且在该XML文档中填充拥有一个最受欢迎以及其他两个或多个餐馆的城市的数据。检验该 文档的良构性有效性。
3.编写能够按照名称排序显示最受欢迎的餐馆的XML样式表,前提是餐馆必须坐落在人口数大于5000的城市中。
4.编写一个Java程序用来解析XML文档,该程序还需要为餐馆实体生成SQL插入语句,并执行这些插入语句。