数据库的XML API(1) 数据库和XML提供了数据存储的完整功能。数据库保存数据用于高效的数据查询,而XML则提供了一种不同应用间信息交换的简单途径。为了利用XML的优点,我们需要将数据库表转化为XML文档。然后我们便可以使用指定的XML工具对这些文档进行其它处理。例如,XML文档可通过XSLT样式表转化为HTML页显示,或通过如XQL这样基于XML的查询语言进行检索,或作为一种数据交换格式,等等。然而,通常将一个数据库转化为XML文档代价不菲,要包括开始时数据转化的花费及以后数据源同步的花费。 为了能处理XML文档,大多数XML工具使用SAX与DOM编程接口。本文中,我们将看到一种数据库通用编程接口的实现方法,它使得XML工具能象处理XML文档一样处理数据库。通过这种方法,我们可以避免对数据库的XML物理转化。
我们会看到一种用于数据库的SAX编程接口实现,它可通过任何JDBC引擎实现对数据库的操作。然后,我们会看到一种用于数据库的DOM编程接口实现,它是通过SAX编程接口间接实现的。为了演示这种用于数据库的SAX编程接口,我们会看到将其与XT(一种XSLT处理器)的集成。我们同样会看到有关这种集成一个范例,它演示了通过将XSLT样式表如何直接作用于数据库来建立一个HTML页面,及如何将一个数据库转化为一个XML文档。最后,我们会看到如何将用于数据库的DOM编程接口与一个XQL处理器相结合。
本文中,作者利用已有的工具而不是建立一个新的工具来阐明用于数据库的SAX及DOM应用,并显示如何支持众多的XML工具对数据库进行操作。所有本文中提及的XML工具都是免费的(自由软件或非商业用途免费),当然,即使如此,你仍然应该好好看一下有关版权的说明。
· SAX与DOM编程接口的概况
SAX是一种基于事件的XML编程接口。通过它,SAX解析器可搜索一个XML文档,并告诉应用程序如某元素的开始与结束等事件。由于解析器是通过查看XML文档的不同部分来产生事件的,因此不需要建立任何内部的结构。这大大减少了对系统资源的需求,并且对那些较大的XML文档的解析非常合适。对于那些以接收数据流形式处理的XML文档,基于事件的XML编程接口是唯一选择。
另一方面,DOM编程接口采用的是一种树型结构。元素之间具有亲子关系,通过DOM编程接口,解析器基于XML文档建立一个内部结构,从而使应用可以通过树型模式对其进行操作。DOM允许一个应用随意访问树型结构文档,代价是增加了内存的负荷。
· 面向数据库XML编程接口:基本内容
由于数据库具有高度规范的数据存储结构,因此我们可以将其映射为以数据为中心的XML文档。例如,我们可以通过如下的DTD范例转化一个数据库为XML文档:
<!ELEMENT table rows*> <!ELEMENT rows (column1, column2, ...)> <!ELEMENT column1 #PCDATA> <!ELEMENT column2 #PCDATA> .... 换句话说,通过一个XML数据库编程接口,我们可以使数据库看起来像一个XML文档:即使用API将数据库封装为一个虚拟的XML文档。这里我们使用了面向对象设计的基本概念:即我们提供的是一个接口,而不是方法的实现。从应用的角度,使用这种XML数据库编程接口的工具并不关心它们处理的实际是XML文档或是一个数据库表。
· 面向数据库的SAX编程接口实现
为了实现数据库用的SAX编程接口,我们需要实现一个基于JDBC的解析器,遍历数据源的每一行与列,并产生适当的SAX事件。SAX规范提供了org.xml.sax.InputSource类,它可以将一个数据源以一个URL或一个数据字节流的方式引用。我们可以使用JDBCInputSource,它扩展了org.xml.sax.InputSource类,以下是JDBCInputSource的详细内容:
// JDBCInputSource.Java package dbxml.sax; import java.sql.*; import org.xml.sax.InputSource; public class JDBCInputSource extends InputSource { private String _connectionURL; private String _userName; private String _passwd; private String _tableName; public JDBCInputSource(String connectionURL, String userName, String passwd, String tableName) { super(connectionURL); _connectionURL = connectionURL; _userName = userName; _passwd = passwd; _tableName = tableName; } public String getTableName() { return _tableName; } public Connection getConnection() throws SQLException { return DriverManager.getConnection(_connectionURL, _userName, _passwd); } }
在上述的代码中,构造函数使用了数据库连接所需的信息及将被解析的数据库表名。方法getConnection()连接数据库并返回一个连接对象。
下一步,我们需要通过JDBCInputSource实现SAX解析器,并遍历数据库表的行与列,产生SAX事件。为了简化代码,我们创建了一个抽象的ParserBase类,它实现了org.xml.sax.Parser类并负责管理不同的句柄。然后我们建立一个基于JDBC的SAX解析器JDBCSAXParser,它扩展了ParserBase类:
(To view the code for ParserBase.java, click here.) // JDBCSAXParser.java package dbxml.sax; import java.io.IOException; import java.sql.*; import org.xml.sax.*; import org.xml.sax.helpers.AttributeListImpl; public class JDBCSAXParser extends ParserBase { private static final AttributeList _stockEmptyAttributeList = new AttributeListImpl(); //------------------------------------------------------------------ // Methods from the Parser interface //------------------------------------------------------------------ public void parse (InputSource source) throws SAXException, IOException { if (! (source instanceof JDBCInputSource)) { throw new SAXException('JDBCSAXParser can work only with source ' + 'of JDBCInputSource type'); } parse((JDBCInputSource)source); }
public void parse (String systemId) throws SAXException, IOException { throw new SAXException('JDBCSAXParser needs more information to ' + 'connect to database'); }
//------------------------------------------------------------------ // Additional methods //------------------------------------------------------------------ public void parse(JDBCInputSource source) throws SAXException, IOException { try { Connection connection = source.getConnection(); if (connection == null) { throw new SAXException('Could not establish connection with ' + 'database'); }
String sqlQuery = getSelectorSQLStatement(source.getTableName()); PreparedStatement pstmt = connection.prepareStatement(sqlQuery);
ResultSet rs = pstmt.executeQuery(); parse(rs, source.getTableName()); rs.close();
connection.close(); } catch (SQLException ex) { throw new SAXException(ex); } }
public void parse(ResultSet rs, String tableName) throws SAXException, SQLException, IOException { if (_documentHandler == null) { return; // nobody is interested in me, no need to sweat! }
ResultSetMetaData rsmd = rs.getMetaData(); int numCols = rsmd.getColumnCount();
String tableMarker = getTableMarker(tableName); String rowMarker = getRowMarker();
_documentHandler.startDocument(); _documentHandler.startElement(tableMarker, _stockEmptyAttributeList); while(rs.next()) { _documentHandler.startElement(rowMarker, _stockEmptyAttributeList); for (int i = 1; i <= numCols; i++) { generateSAXEventForColumn(rsmd, rs, i); } _documentHandler.endElement(rowMarker); } _documentHandler.endElement(tableMarker); _documentHandler.endDocument(); }
public void parse(String connectionURL, String userName, String passwd, String tableName) throws SAXException, IOException { parse(new JDBCInputSource(connectionURL, userName, passwd, tableName)); }
//------------------------------------------------------------------ // Protected methods that derived classes could override to // customize the parsing. //------------------------------------------------------------------ protected void generateSAXEventForColumn(ResultSetMetaData rsmd, ResultSet rs, int columnIndex) throws SAXException, SQLException { String columnvalue = rs.getString(columnIndex); if (columnvalue == null) { return; } String columnMarker = getColumnMarker(rsmd.getColumnLabel(columnIndex)); char[] columnvalueChars = columnvalue.toCharArray(); _documentHandler.startElement(columnMarker, _stockEmptyAttributeList); _documentHandler.characters(columnvalueChars, 0, columnvalueChars.length); _documentHandler.endElement(columnMarker); }
protected String getTableMarker(String tableName) { return tableName; } protected String getRowMarker() { return 'row'; } protected String getColumnMarker(String columnName) { return columnName; } protected String getSelectorSQLStatement(String tableName) { return 'select * from ' + tableName; } }
(未完待续)
|