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插入語句,並執行這些插入語句。