編寫映射文件
映射文件是一個特殊的XML文件,用戶在其中指定XML元素、屬性(attributes)以及PCDATA如何映射到數據庫的表與列。清單A就是這個XML示例的映射文件,清單B是
數據庫的一個SQL架構。在清單A中,Options元素包含系統特有的一些參數。在這例子中,你要設置相應的格式,以實現DATE類型的數據庫字段與
XML數據的相互轉換。注意,Pattern屬性必須遵循java.text.SimpleDateFormat模式規范。
清單A:
Listing A |
|
|
<?xml version='1.0' ?>
<XMLToDBMS Version="2.0" xmlns="http://www.xmlmiddleware.org/xmldbms/v2">
<Options>
<SimpleDateFormat Pattern="yyyy-MM-dd" DefaultForTypes="DATE" />
</Options>
<Databases>
<Database Name="Default">
<Catalog>
<Schema>
<Table Name="users">
<Column Name="user_id" DataType="VARCHAR" Length="24" Nullable="No"/>
<Column Name="company" DataType="VARCHAR" Length="255" Nullable="No"/>
<PrimaryKeyKeyGenerator="UID">
<UseColumn Name="user_id"/>
</PrimaryKey>
</Table>
<Table Name="orders">
<Column Name="order_id" DataType="VARCHAR" Length="24" Nullable="No"/>
<Column Name="user_id" DataType="VARCHAR" Length="24" Nullable="No"/>
<Column Name="posted_at" DataType="DATE" Nullable="No"/>
<Column Name="type" DataType="INTEGER" Nullable="Yes"/>
<ForeignKey Name="order_to_user_FK">
<UseTable Name="users" />
<UseUniqueKey Name="PrimaryKey" />
<UseColumn Name="user_id"/>
</ForeignKey>
<PrimaryKeyKeyGenerator="UID">
<UseColumn Name="order_id"/>
</PrimaryKey>
</Table>
<Table Name="cargos">
<Column Name="cargo_id" DataType="VARCHAR" Length="24" Nullable="No"/>
<Column Name="order_id" DataType="VARCHAR" Length="24" Nullable="No"/>
<Column Name="title" DataType="VARCHAR" Length="255" Nullable="Yes"/>
<Column Name="code" DataType="VARCHAR" Length="255" Nullable="Yes"/>
<Column Name="quantity" DataType="INTEGER" Nullable="Yes"/>
<Column Name="weight" DataType="INTEGER" Nullable="Yes"/>
<Column Name="danger" DataType="VARCHAR" Length="255" Nullable="Yes"/>
<PrimaryKeyKeyGenerator="UID">
<UseColumn Name="cargo_id"/>
</PrimaryKey>
<ForeignKey Name="cargo_to_order_FK">
<UseTable Name="orders" />
<UseUniqueKey Name="PrimaryKey" />
<UseColumn Name="order_id"/>
</ForeignKey>
</Table>
</Schema>
</Catalog>
</Database>
</Databases>
<Maps>
<ClassMap>
<ElementType Name="order"/>
<ToClassTable Name="orders"/>
<PropertyMap>
<ElementType Name="order_id"/>
<ToColumn Name="order_id"/>
</PropertyMap>
<PropertyMap>
<ElementType Name="user_id"/>
<ToColumn Name="user_id"/>
</PropertyMap>
<PropertyMap>
<ElementType Name="posted_at"/>
<ToColumn Name="posted_at"/>
</PropertyMap>
<PropertyMap>
<ElementType Name="type"/>
<ToColumn Name="type"/>
</PropertyMap>
<RelatedClassKeyInParentTable="Unique">
<ElementType Name="cargo"/>
<UseUniqueKey Name="PrimaryKey"/>
<UseForeignKey Name="cargo_to_order_FK"/>
</RelatedClass>
</ClassMap>
<ClassMap>
<ElementType Name="cargo"/>
<ToClassTable Name="cargos"/>
<PropertyMap>
<ElementType Name="cargo_id"/>
<ToColumn Name="cargo_id"/>
</PropertyMap>
<PropertyMap>
<ElementType Name="order_id"/>
<ToColumn Name="order_id"/>
</PropertyMap>
<PropertyMap>
<ElementType Name="title"/>
<ToColumn Name="title"/>
</PropertyMap>
<PropertyMap>
<ElementType Name="code"/>
<ToColumn Name="code"/>
</PropertyMap>
<PropertyMap>
<ElementType Name="quantity"/>
<ToColumn Name="quantity"/>
</PropertyMap>
<PropertyMap>
<ElementType Name="weight"/>
<ToColumn Name="weight"/>
</PropertyMap>
<PropertyMap>
<ElementType Name="danger"/>
<ToColumn Name="danger"/>
</PropertyMap>
</ClassMap>
</Maps>
</XMLToDBMS>
|
清單B:
Listing B |
 |
 |
CREATE TABLE XMLDBMSKey ( HighKeyint(11) ); CREATE TABLE orders ( order_idvarchar(24) DEFAULT '' NOT NULL, user_idvarchar(24) DEFAULT '' NOT NULL, posted_atdate, type integer, PRIMARY KEY (order_id) ); CREATE TABLE cargos ( cargo_idvarchar(24) DEFAULT '' NOT NULL, order_idvarchar(24) DEFAULT '' NOT NULL, title varchar(255), code varchar(255), quantity integer, weight integer, danger varchar(255), PRIMARY KEY (cargo_id) ); |
清單A的Databases元素包含數據庫的一個關系架構。該構架確保XML-DBMS能正確地映射數據并編譯映射文件(如果要在多個映射文件中使用相同的數據庫架構,最好的辦法就是在每個文件中將架構作為一個XML外部實體來包容,不必每次都進行重寫)。
數據庫名稱“Default”意味著數據庫必須在JDBC連接URL中顯式地指定。必須在每個表中提供主鍵信息,而且每個鍵都必須存在于數據庫中。否則,編譯器可能報告一個映射異常。
在KeyGenerator中,你需要提供主鍵生成器的名稱,同時必須用一個獨立的Java類來實現這個生成器。KeyGenerator是Java接口的一個實現,每次執行一個INSERT操作時,它都會生成一個惟一的鍵值。也可使用一個簡單的、現成的生成器。
圖B的XMLDBMSKey表包含了由KeyGenerator使用的值。UseColumn元素指向一個充當主鍵的元素,并告訴程序在哪里寫生成
的鍵值。除非Name屬性明確指定了一個名稱,否則主鍵會隱式地采用PrimaryKey這一默認名稱。主鍵可能同時使用了多列,
KeyGenerator的實現必須知道它應該生成多少個鍵。
清單A的ForeignKey元素用于描述元素類之間的主鍵–外鍵關系。
在映射文件中,最重要的一部分就是Maps小節。元素類型及其內容通常被視為一個類,并映射到一個表。這是通過一個ClassMap元素及其子元素
來完成的。子元素包括ElementType,其中含有要映射的元素的名稱;以及ToClassTable,它指定元素要映射到哪個表。
PropertyMap元素能控制“簡單”(子節點中的PCDATA)元素或屬性(attribute)映射——雖然在這個例子中,應用程序映射的
只是元素。RelatedClass元素允許我們在此封裝另一個元素類,但那個類必須在另一個ClassMap元素中得到定義。
映射文檔編譯成XMLDBMap Java對象,最終的結果會傳給DBMSToDOM、DOMToDBMS或者DBMSDelete對象。這些特殊對象負責與選擇、插入、更新或刪除數據有關的全部工作。
編寫過濾器和動作文件
過濾器文檔由一系列過濾器構成,這些過濾器應用于數據庫中的值。這樣一來,過濾器就能過濾由SELECT操作檢索的行或者由DELETE操作刪除的
行。過濾器語言位于映射語言的頂端。換言之,過濾器文檔允許你指定數據的檢索條件,而映射語言提供結構化信息(也就是聯接–join)。你可認為映射語言
和過濾器語言合并在一起,共同為數據庫提供了一套簡單的查詢語言,它確保結果以XML的形式返回。過濾器文檔要編譯成FilterSet
Java對象,最終的對象會傳給DBMSToDOM或者DBMSDelete對象。
動作文檔由一系列動作構成,這些動作要應用于XML文檔中的值。你需要為作為類來映射的元素類型指定相應的動作。這些動作會轉換為對行的插入、更新
及刪除操作。在一個動作文檔中,如果已經規定特定的行需要插入或更新,就不能再規定那些行需要刪除;反之亦然。如果一個動作文檔規定某些行需要插入或更
新,就會由DOMToDBMS對象來使用那些文檔。相反,如果規定某些行需要刪除,則會由DBMSDelete對象來使用。
Listing C
<?xmlversion='1.0' ?>
<Actions Version="2.0" xmlns="http://www.xmlmiddleware.org/xmldbms/actions/v2">
<DefaultAction>
<None />
</DefaultAction>
<Action>
<ElementType Name="order" />
<UpdateOrInsert />
</Action>
<Action>
<ElementType Name="cargo" />
<UpdateOrInsert />
</Action>
</Actions>
清單C演示了插入/更新動作。這段代碼會使用一個已知的主鍵(如果存在的話)來更新一個行,或者生成一個新的主鍵(如果不存在的話)。實際上,你需要為兩個類(order和cargo)插入元素,因為cargo元素嵌套在order元素中。
Listing D
<?xmlversion='1.0' ?>
<FilterSet Version="2.0" xmlns="http://www.xmlmiddleware.org/xmldbms/filters/v2">
<Options>
<Wrapper Name="orders" />
</Options>
<Filters>
<Filter>
<RootFilter>
<Table Name="orders" />
<Where Condition=" user_id = 'UID745632' "/>
</RootFilter>
</Filter>
</Filters>
</FilterSet>
選擇或刪除數據時,需要使用包含在清單D中的過濾器文件。Options元素包含一個特殊的Wrapper元素,它用于包裝查詢結果。如果會從數據
庫中檢索多個元素,就需要使用Wrapper元素。在這種情況下,生成的是含有多個根元素的XML結構,現行的許多標準都不支持它。
RootFilterelements用于指定從根表檢索到的值。
編寫Java代碼
現在,我們已準備好編寫自己的應用程序,它將在一個關系數據庫中存儲和檢索DOM文檔。清單E包含了示范性的Java代碼,它有必要進一步說明。
Listing E
package test.xmldbms;
importorg.xmlmiddleware.db.*;
import org.xmlmiddleware.utils.XMLMiddlewareException;
importorg.xmlmiddleware.xmldbms.*;
importorg.xmlmiddleware.xmldbms.tools.*;
importorg.xmlmiddleware.xmldbms.actions.*;
importorg.xmlmiddleware.xmldbms.datahandlers.*;
importorg.xmlmiddleware.xmldbms.filters.*;
importorg.xmlmiddleware.xmldbms.keygenerators.*;
importorg.xmlmiddleware.xmldbms.maps.*;
importorg.xmlmiddleware.xmlutils.*;
importorg.xml.sax.*;
import org.w3c.dom.*;
importjava.io.*;
importjava.util.*;
public class XMLToDBMSAndViceVersa
{
// Service objects
private DOMToDBMSdomToDBMS = null;
private DBMSToDOMdbmsToDOM = null;
private DBMSDeletedbmsDelete = null;
// Credentials for connecting to database
private static String JDBC_DRIVER = "org.gjt.mm.mysql.Driver";
private static String JDBC_URL = "jdbc:mysql://localhost/dbname";
private static String JDBC_DBNAME = "dbname";
private static String JDBC_USER = "dbuser";
private static String JDBC_PASSWORD = "dbpassword";
// Some file names
private static String mapFilename = "Listing-A.map";
private static String actionFilename = "Listing-C.act";
private static String filterFilename = "Listing-D.act";
// Datahandler class
private static String GENERICHANDLER = "org.xmlmiddleware.xmldbms.datahandlers.GenericHandler";
// Key generation class
private static String KEYGENERATOR = "org.xmlmiddleware.xmldbms.keygenerators.KeyGenerator";
// Parser utils class; XML-DBMS does not yet support JAXP, so such operations as XML parsing and serializing
// needs to be a little customized for each parser. this is the class for supporting Xerces parser.
private static String PARSERUTILSCLASS = "org.xmlmiddleware.xmlutils.external.ParserUtilsXerces";
private static boolean VALIDATING_PARSER = false;
public static void main(String [] args) {
// Tell the JVM to run finalizers on exit. This is necessary to ensure
// that database connections are properly closed.
System.runFinalizersOnExit(true);
// Creating parser utilities
ParserUtilsutils = (ParserUtils)Class.forName(PARSERUTILSCLASS).newInstance();
// Creating a data source and data handler.
DataSourcedataSource = new JDBC1DataSource(JDBC_DRIVER, JDBC_URL);
dataHandler = (DataHandler)Class.forName(GENERICHANDLER).newInstance();
dataHandler.initialize(dataSource, JDBC_USER, JDBC_PASSWORD);
// Compiling and instantiating a Map object
MapCompiler compiler = new MapCompiler(utils.getXMLReader(VALIDATING_PARSER));
XMLDBMSMap map = compiler.compile(new InputSource(new FileReader(mapFilename)));
// Create an object containing information needed to transfer data
TransferInfotransferInfo = new TransferInfo(map);
transferInfo.addDataHandler(JDBC_DBNAME, dataHandler);
// Compiling and instantiating an Action object
ActionCompiler compiler = new ActionCompiler(utils.getXMLReader(VALIDATING_PARSE));
Actions actions = compiler.compile(map, new InputSource(new FileReader(actionFilename)));
// Creating and configuring service object
domToDBMS = new DOMToDBMS();
domToDBMS.setCommitMode(DataHandler.COMMIT_AFTERSTATEMENT);
domToDBMS.stopOnException(true);
domToDBMS.setFilterSetReturned(false);
KeyGeneratorkeyGen = (KeyGenerator)Class.forName(KEYGENERATOR).newInstance();
domToDBMS.addKeyGenerator("UID", keyGen); // Adding used in Listing-A key generator named UID
// Getting our XML document into DOM document
Document doc = utils.openDocument(new InputSource(new StringReader(xmlString)),VALIDATING_PARSER);
// Ooops... and our XML file is already in the database!
domToDBMS.storeDocument(transferInfo, doc, actions);
// Creating FilterSet object for retrieving data
FilterCompiler compiler = new FilterCompiler(utils.getXMLReader(validate));
FilterSetfilterSet = compiler.compile(map, new InputSource(new FileReader(filterFilename)));
// Creating and configuring another service object
dbmsToDOM = new DBMSToDOM(utils);
dbmsToDOM.setDTDInfo(null, null);
Hashtableparams = new Hashtable();
// And now we are getting a DOM document from database
doc = dbmsToDOM.retrieveDocument(transferInfo, filterSet, params, null);
}
}
首先,你必須實例化一個數據源和一個數據處理程序。JDBC1DataSource類是JDBC 2.0
DataSource的一個實現,它用于一個遵循JDBC1標準的驅動程序,而且提供了對連接池的支持。Datahandler是用于對數據庫訪問進行抽
象的一個接口。你還要指示JVM在退出時運行終結器(finalizers),這是用System.runFinalizersOnExit(true)
來實現的。終結器必須運行,否則無法保證數據庫連接正確關閉。ParserUtils是實現了解析器特有方法的一個類的接口。之所以需要它,是因為XML
解析和序列化操作需要針對每一個解析器進行少許定制。
你必須編譯和實例化一個Map對象、Actions對象以及FilterSet對象,它們全都會在文檔轉換過程中使用。為此,你需要使用由
MapCompiler、ActionCompiler和FilterCompiler等對象提供的相應的方法。TransferInfo對象包含在
XML文檔和數據庫之間傳輸數據所需的信息。它封裝了根據一個特定的映射,在XML文檔和數據庫之間傳輸數據所需的映射元數據以及DataHandler
對象。它為每個數據庫都包含單獨一個XMLDBMSMap對象和一個DataHandler對象。你還必須創建一個KeyGenerator實現對象,以
便在插入新對象時生成主鍵。鍵用于聯接不同的表(從類表到類表,或者從類表到屬性表),還用于從根表(root tables)檢索數據。
最后,你可創建一個DOMToDBMS對象,以便將數據從一個DOM樹傳輸到數據庫。采取類似的方式,還可創建一個DBMSToDOM對象,以便將關系數據傳回DOM。
總結
本文證明使用XML-DBMS來存儲XML數據其實并不難。如果你已經建立了一個關系數據庫基礎結構,或者希望建立一個獨立于數據庫廠商的基礎結
構,就推薦采取這種方式。由于XML-DBMS不要求任何特定的數據庫,所以你的數據庫只需理解標準SQL,而且有一個JDBC驅動程序(或者具有橋接機
制的一個
ODBC驅動程序)就可以了。如果你的應用程序需要搜索或者合并來自不同類型的數據源的信息,就適合使用這個框架,因為關系數據庫架構很容易通過XML
DTD和XML架構來建立,另外還有大量工具可將DTD和架構轉換成映射文件。另外,還可用它生成由數據驅動的應用程序,比如一個CMS或者CRM系統。
XML-DBMS是從XML到DBMS的中間件產品的一個典范,能有效地整合支持和不支持XML的系統。