编写映射文件
映射文件是一个特殊的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的系统。