让我们来看看上述代码的详细内容。JDBCSAXParser包括了几个重载的parse()方法。在下表中org.xml.sax.Parser接口需要实现parse(InputSource)与parse(String)方法。其它parse()方法简化了代码并允许通过派生类重载以改变其解析方法。
如果参数为JDBCInputSource类型,Parse(InputSource)方法调用parse(JDBCInputSource)方法,否则,产生一个SAXException事件表示无法处理数据源。
当所提供的信息不足以访问数据库时parse(String)方法产生一个SAXException事件。 Parse(JDBCInputSource)方法对输入源建立一个连接对象,并执行一个查询以获得一个ResultSet对象。然后对该对象可调用parse(ResultSet)方法。
Parse(ResultSet,String)方法执行解析的核心逻辑。它遍历结果集中的每一行与字段。在对每一行循环时调用方法StartElement()与endElement()(将数据库表标识作为元素名参数)。同样的,在对每一行记录的每个字段循环时会调用方法StartElement()与endElement()(将行标识作为元素名参数)。在上述两种情况中一个空的属性表作为第二参数传递给startElement()。在访问记录的每个字段时,方法generateSAXEventForColumn()被调用(使用字段名与字段值为参数)。通过对结果集对象使用getString90方法可获得单个字段的值,同样我们需要用一个字符串表征字段数据,并在characters()事件中使用。
方法parse(String, String, String, String)通过传递给它的参数简单有效地建立了一个JDBCInputSource对象,然后可对该对象使用parse(JDBCInputSource)方法。 方法JDBCSAXParser(protected方式)通过过载提供了一些专门的特性:
方法generateSAXEventForColumn()为字段数据产生事件。一个数据库中的字段空值(null)与一个字段零值(empty)有着不同的含义。我们通过过滤那些具有null值的字段来捕获这种差别。另一种表现数据库中null值的方法是使用一个二进制属性(如isNull)。通过该选项,一个真值(true)被认为是null值,否则就不是。
GetTableMarker(),getRowMarker(),及getClumnMarker()方法可分别返回合适的表、行、字段默认值。派生出来的类可重载这些方法以提供特定的标识。
方法GetTableMarker()返回一个“select * from <tableName>”字符串。派生出来的类可通过重载该方法以提供一个不同的select query字符串,并实现数据库级的过滤。
类JDBCSAXUtils提供两种方法来建立一个JDBCInputSource对象:通过属性(property)文件或一个Property对象。它不需要通过SAX或DOM编程接口提供一个有关数据库的参数表给应用程序。它希望用户来提供一个包含完整数据库URL入口的属性文件,一个可连接到数据库的用户名及密码,一个用于建立连接JDBC数据库引擎,及数据库表名。以下是一个典型的属性文件:
# portfolio.prop # JDBCSAXSource property file URL=jdbc:odbc:db1 user=jw password=jw-passwd table=portfolio driver=sun.jdbc.odbc.JdbcOdbcDriver
我们现在有了一个简单的解析器,它能对数据库表产生适当的SAX事件。它能区分null值并提供一些专用的标识。这些功能对于一些应用已经足够了,而某些完整的解决方案还需要一些附加的功能,这是因为:
解析器不能合并那些关联的信息,该问题的解决可通过使用Xpointer/Xlink来设置表中外键的相关信息。 数据库中的一个text字段可能会包含标识(marked-up)信息。一个用于数据库的SAX解析器应该能解析这类数据并产生适当的SAX事件。如果这类功能对某个应用非常重要,可以通过重载generateSAXEventForColumn()方法并解析该字段的内容以产生附加的SAX事件
在数据库中,一个数据库表包含了未排序的字段集;有关字段存储的排序并不重要。另一方面,一个XML DTD,无法描述一个未排序的子元素集。
我们可以通过几种方法处理该问题。如果我们要将数据库转化为另一种XML文档,比如说一个HTML页面,为其定制的XSLT样式表可建立正确排序的输出结果。我们也可以重载getSelectorSQLStatement()方法直接提供一个正确排序的字段列表。
有时我们希望通过某些查询能将一个表的被选择部分作为一个XML文档封装。如果XML工具能实现这样的过滤,我们就可以更好的使用数据库。方法getSelectorSQLStatement()可以通过重载并返回合适的select结果字符串。
解析器通过对结果集(result-set)对象使用getString()方法可获得字符串形式的某个字段值。该操作对于文本、数字等类型的字段非常合适,但对于二进制数据就不适合。当使用文本表示二进制数据时,在某些操作中会无法使用。解析器同样无法处理某些可被SQL3/JDBC2.0接受的用户自定义的数据类型。
对于上述问题我们可以通过重载generateSAXEventForCloumn()方法以及提供一种合适的处理(implementation)来实现。
面向数据库的DOM编程接口实现 为了建立一个对应于一个数据库表的DOM树,我们可以遍历每一行每个字段并为其建立树结点,或者我们可以通过其它的类库,如Sun的JAXP工具,它可以通过一个SAX事件流建立一个DOM树。后一个方法更简单,代码更简练,因为它利用了一个已有的功能。为了通过这种方法实现DOM编程接口,我们需要一个合适的SAX数据库解析器,使我们的实现更方便。 将DOM数据库编程接口与XQL处理器相集成 通过类JDBCDOMParser实现面向数据库的DOM编程接口:
// JDBCDOMParser.java package dbxml.dom; import java.io.IOException; import org.w3c.dom.Document; import org.xml.sax.SAXException; import com.sun.xml.tree.XmlDocumentBuilder; import dbxml.sax.*; public class JDBCDOMParser { public static Document createDocument(JDBCInputSource inputSource) throws SAXException, IOException { XmlDocumentBuilder documentBuilder = new XmlDocumentBuilder(); JDBCSAXParser saxParser = new JDBCSAXParser(); documentBuilder.setParser(saxParser); saxParser.parse(inputSource); return documentBuilder.getDocument(); } }
有关类JDBCDOMParser的实现较为简单,它通过JAXP所提供的XmlDocumentBuilder类可以从一个SAX事件流构建一个DOM文档。JDBCDOMParser仅有一个方法:createDocument(),它需要一个JDBC数据源作为参数。该方法建立一个JDBCSAXParser并可用其来解析一个实际的XmlDocumentBuilder对象。然后它释放解析对象并返回XmlDocumentBuilder对象中产生的结果。在实际编程实现中,XmlDocumentBuilder对象通过建立一个DOM文档的方法来响应JDBCSAXParser对象所产生的SAX事件。
· 利用面向数据库的SAX编程接口
我们已经看了一个通过面向数据库的SAX编程接口来实现DOM编程接口的实例。现在我们要看另一个使用SAX编程接口的例子。在本节中,我们将看到如何将SAX数据库编程接口与XT(一个用Java写的XSLT处理器)相集成。通过这种集成,我们可以对一个存储在数据库中的虚拟XML文档直接使用XSLT样式表。
我们封装了实际建立一个SAX数据库源的逻辑,并用给定的XSLT样式表加以处理,在类JDBCXSLProcessor中产生一个输出文件(使用XT中的com.jclark.xsl.sax.Driver)。其中主要的方法包含三个参数:一个数据库特征文件,一个XSLT样式表文件,一个输出文件。 如我们即将在下面看到的,我们可以通过这种方法直接生成HTML页面而不需要一个临时的XML文件来过渡。另外,我们还将看到如何将SAX数据库编程接口与XT相结合,以将一个数据库转化为一个物理的XML文档。
(要查看有关JDBCXSLProcessor.java的代码, 请点击此处.) 利用XSLT样式表从数据库直接生成HTML页面 现在我们来看一个简单的样式表,它可以将一个高度规范的XML文档(基于数据库表)格式化显示。该数据库表将被格式化为一个HTML表。样式表createTable.xsl可以被用于处理任何具有表状结构的XML文档。该样式表使用字段的标记名作为表头标题。
(要查看关于createTable.xsl的源代码, 请点击此处.) 通过一个XSLT样式表将一个数据库转化为一个XML文档 尽管大多数XML应用程序可以直接使用SAX或DOM编程接口,不过在某些场合我们仍需要得到一个实际的XML文档。举例而言,对于某个不使用任何一种XML API的工具,我们就需要一个物理XML文档。在这里,我们推荐一种由数据库生成XML文档的方法。在这种方法中,我们制定一个XSLT样式表来实现统一的转换。使用这样一个样式表(与一个SAX数据库编程接口相结合),我们可以生成一个与数据库表相对应的XML文档。在此,作者提供了一个样式表??identity.xsl??可用于在当前版本的XT中,以实现数据的统一转换。
identity.xsl <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:template match="@*|*"> <xsl:copy> <xsl:apply-templates select="@*|node()"/> </xsl:copy> </xsl:template> </xsl:stylesheet>
注意虽然一个XSLT样式表可以容易地实现数据的成批转换,不过这种方法并不十分有效,因为一个完整的样式表其逻辑也许会非常复杂,从而影响样式表的建立及实际数据转换的效率。尤其当数据库表中包含大量记录时这个问题会特别明显。一个折衷的方法是写一个专门用于批量转换的应用程序。这样一个应用程序可以监听SAX事件并建立XML元素(及对应的实际数据),所产生的结果XML文档与数据库表相对应。
· 使用DOM数据库编程接口
在大多数场合,SAX数据库编程接口较DOM编程接口更节约系统资源。不过,某些应用程序要求随意的访问XML文档,因此就会需要提供一个类似与DOM的树形结构来表示数据库。
· 将DOM数据库编程接口与XQL处理器相集成
XML Query Language(XQL)是一种用于XML文档查找的语言,其语法与Xpath方式相近。在这里,我们将我们的DOM数据库解析器与GMD-IPSI'S XQL Engine相集成。通过这种集成我们可以对一个表征数据库表的XML文档执行类似于SQL的查询。
作为一个集成的例子,作者提供一个简单封装的用于查询一个基于数据库表的XML文档。类JDBCXQLProcessor建立一个类似于封装的环境,它接收客户的查询并输出结果文档。方法PprocessQueries()可以操作任何文档对象??不仅是由JDBCDOMParser所建立的对象。它读系统,执行查询请求并输出系统的查询结果。Main()方法可通过其参数建立一个JDBCInputSource对象并将其传递给JDBCDOMParser以获得一个与所给数据库表相对应的文档对象。
(要查看JDBCXQLProcessor的Java代码,请点击此处.) 另外说明一点,通过XMLWriter与JDBCDOMParser写一个数据库到XML的转换采用的是快照方式。即从JDBCDOMParser得到一个文档对象并通过XMLWriter.write(Document)写到目标文件中。
· 结论
在本文中,我们讨论了如何通过面向数据库的XML编程接口来表征数据库中的信息。通过这种编程接口,我们可以避免将一个数据库转换为一个实际的XML文档以及避免保持二者间的同步。我们介绍了一种通过Java实现的SAX与DOM数据库编程接口,然后介绍了一种与XT相集成的SAX编程接口。我们演示了通过这种集成由数据库表直接导出HTML页面以及将数据库表转换为XML文档。最后,我们将DOM数据库编程接口与一个XQL处理器相集成。
· 关于作者
Ramnivas Laddad 是一个Java的SUN认证工程师。他有一个电子工程通讯技术专业的硕士学位,有六年的软件开发经验(关于网络,图形用户界面,分布式系统等等)。在面向对象的软件系统方面有丰富经验(五年C++,两年Java)。Ramnivas现在http://www.rti.com/工作,致力于设计与研究ControlShell(一种用于复杂实时监控系统的模块化编程框架技术)。
|