編寫映射文件
映射文件是一個特殊的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> |
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的系統。