[转]数据库的XML API(一)
来源:互联网 酷勤网整理
数据库和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;
}
}
(未完待续)