数据库的 XML API

数据库和&nbspXML 提供了数据存储的完整功能。数据库保存数据用于高效的数据查询,而&nbspXML 则提供了一种不同应用间信息交换的简单途径。为了利用&nbspXML 的优点,我们需要将数据库表转化为&nbspXML 文档。然后我们便可以使用指定的 &nbspXML 工具对这些文档进行其它处理。例如,XML 文档可通过&nbspXSLT 样式表转化为& nbspHTML 页显示,或通过如&nbspXQL 这样基于&nbspXML 的查询语言进行检索,或作为一种数据交换格式,等等。然而,通常将一个数据库转化为&nbspXML 文档代价不菲,要包括开始时数据转化的花费及以后数据源同步的花费。

  为了能处理&nbspXML 文档,大多数&nbspXML 工具使用&nbspSAX 与&nbspDOM 编程接口。本文中,我们将看到一种数据库通用编程接口的实现方法,它使得&nbspXML 工具能象处理&nbspXML 文档一样处理数据库。通过这种方法,我们可以避免对数据库的&nbspXML 物理转化。

  我们会看到一种用于数据库的&nbspSAX 编程接口实现,它可通过任何&nbspJDBC 引擎实现对数据库的操作。然后,我们会看到一种用于数据库的&nbspDOM 编程接口实现,它是通过&nbspSAX 编程接口间接实现的。为了演示这种用于数据库的&nbspSAX 编程接口,我们会看到将其与& nbspXT(一种&nbspXSLT 处理器)的集成。我们同样会看到有关这种集成一个范例,它演示了通过将&nbspXSLT 样式表如何直接作用于数据库来建立一个&nbspHTML 页面,及如何将一个数据库转化为一个&nbspXML 文档。最后,我们会看到如何将用于数据库的&nbspDOM 编程接口与一个&nbspXQL 处理器相结合。

  本文中,作者利用已有的工具而不是建立一个新的工具来阐明用于数据库的&nbspSAX 及&nbspDOM 应用,并显示如何支持众多的& nbspXML 工具对数据库进行操作。所有本文中提及的&nbspXML 工具都是免费的(自由软件或非商业用途免费),当然,即使如此,你仍然应该好好看一下有关版权的说明。

  SAX 与&nbspDOM 编程接口的概况

  SAX 是一种基于事件的&nbspXML 编程接口。通过它,SAX 解析器可搜索一个&nbspXML 文档,并告诉应用程序如某元素的开始与结束等事件。由于解析器是通过查看&nbspXML 文档的不同部分来产生事件的,因此不需要建立任何内部的结构。这大大减少了对系统资源的需求,并且对那些较大的&nbspXML 文档的解析非常合适。对于那些以接收数据流形式处理的&nbspXML 文档,基于事件的& nbspXML 编程接口是唯一选择。

  另一方面,DOM 编程接口采用的是一种树型结构。元素之间具有亲子关系,通过& nbspDOM 编程接口,解析器基于&nbspXML 文档建立一个内部结构,从而使应用可以通过树型模式对其进行操作。DOM 允许一个应用随意访问树型结构文档,代价是增加了内存的负荷。

  面向数据库&nbspXML 编程接口:基本内容

  由于数据库具有高度规范的数据存储结构,因此我们可以将其映射为以数据为中心的&nbspXML 文档。例如,我们可以通过如下的&nbspDTD 范例转化一个数据库为XML文档:

  <!ELEMENT&nbsptable&nbsprows*>

  <!ELEMENT&nbsprows (column1,&nbspcolumn2,&nbsp...)>

  <!ELEMENT&nbspcolumn1 #PCDATA>

  <!ELEMENT&nbspcolumn2 #PCDATA>

  ....

  换句话说,通过一个&nbspXML 数据库编程接口,我们可以使数据库看起来像一个&nbspXML 文档:即使用& nbspAPI 将数据库封装为一个虚拟的&nbspXML 文档。这里我们使用了面向对象设计的基本概念:即我们提供的是一个接口,而不是方法的实现。从应用的角度,使用这种&nbspXML 数据库编程接口的工具并不关心它们处理的实际是&nbspXML 文档或是一个数据库表。

  面向数据库的&nbspSAX 编程接口实现

  为了实现数据库用的&nbspSAX 编程接口,我们需要实现一个基于&nbspJDBC 的解析器,遍历数据源的每一行与列,并产生适当的&nbspSAX 事件。SAX 规范提供了&nbsporg.xml.sax.InputSource 类,它可以将一个数据源以一个&nbspURL 或一个数据字节流的方式引用。我们可以使用&nbspJDBCInputSource,它扩展了& nbsporg.xml.sax.InputSource 类,以下是&nbspJDBCInputSource 的详细内容:

  //&nbspJDBCInputSource.java

  package&nbspdbxml.sax;

  import&nbspjava.sql.*;

  import&nbsporg.xml.sax.InputSource;

  public&nbspclass&nbspJDBCInputSource&nbspextends&nbspInputSource {

    private&nbspString&nbsp_connectionURL;

    private&nbspString&nbsp_userName;

    private&nbspString&nbsp_passwd;

    private&nbspString&nbsp_tableName;

    public&nbspJDBCInputSource(String&nbspconnectionURL,&nbspString&nbspuserName,

    String&nbsppasswd,&nbspString&nbsptableName) {

      super(connectionURL);

      _connectionURL =&nbspconnectionURL;

      _userName =&nbspuserName;

      _passwd =&nbsppasswd;

      _tableName =&nbsptableName;

    }

    public&nbspString&nbspgetTableName() {

      return&nbsp_tableName;

    }

    public&nbspConnection&nbspgetConnection()&nbspthrows&nbspSQLException {

      return&nbspDriverManager.getConnection(_connectionURL,&nbsp_userName,&nbsp_passwd);

    }

  }

  在上述的代码中,构造函数使用了数据库连接所需的信息及将被解析的数据库表名。方法getConnection() 连接数据库并返回一个连接对象。
下一步,我们需要通过&nbspJDBCInputSource 实现&nbspSAX 解析器,并遍历数据库表的行与列,产生&nbspSAX 事件。为了简

  代码,我们创建了一个抽象的&nbspParserBase 类,它实现了&nbsporg.xml.sax.Parser 类并负责管理不同的句柄。然后我们建立一个基于&nbspJDBC 的&nbspSAX 解析器&nbspJDBCSAXParser,它扩展了&nbspParserBase 类:

  (To&nbspview&nbspthe&nbspcode&nbspfor&nbspParserBase.java,&nbspclick&nbsphere.)

  //&nbspJDBCSAXParser.java

  package&nbspdbxml.sax;

  import&nbspjava.io.IOException;

  import&nbspjava.sql.*;

  import&nbsporg.xml.sax.*;

  import&nbsporg.xml.sax.helpers.AttributeListImpl;

  public&nbspclass&nbspJDBCSAXParser&nbspextends&nbspParserBase {

    private&nbspstatic&nbspfinal&nbspAttributeList&nbsp_stockEmptyAttributeList

    =&nbspnew&nbspAttributeListImpl();

    //------------------------------------------------------------------

    //&nbspMethods&nbspfrom&nbspthe&nbspParser&nbspinterface

    //------------------------------------------------------------------

    public&nbspvoid&nbspparse (InputSource&nbspsource)&nbspthrows&nbspSAXException,&nbspIOException {

      if (! (source&nbspinstanceof&nbspJDBCInputSource)) {

        throw&nbspnew&nbspSAXException("JDBCSAXParser&nbspcan&nbspwork&nbsponly&nbspwith&nbspsource "

        + "of&nbspJDBCInputSource&nbsptype");

      }

      parse((JDBCInputSource)source);

    }

    public&nbspvoid&nbspparse (String&nbspsystemId)&nbspthrows&nbspSAXException,&nbspIOException {

      throw&nbspnew&nbspSAXException("JDBCSAXParser&nbspneeds&nbspmore&nbspinformation&nbspto "

      + "connect&nbspto&nbspdatabase");

    }

    //------------------------------------------------------------------

    //&nbspAdditional&nbspmethods

    //------------------------------------------------------------------

    public&nbspvoid&nbspparse(JDBCInputSource&nbspsource)

    throws&nbspSAXException,&nbspIOException {

      try {

        Connection&nbspconnection =&nbspsource.getConnection();

        if (connection ==&nbspnull) {

          throw&nbspnew&nbspSAXException("Could&nbspnot&nbspestablish&nbspconnection&nbspwith "

          + "database");

        }

        String&nbspsqlQuery =&nbspgetSelectorSQLStatement(source.getTableName());

        PreparedStatement&nbsppstmt =&nbspconnection.prepareStatement(sqlQuery);

        ResultSet&nbsprs =&nbsppstmt.executeQuery();

        parse(rs,&nbspsource.getTableName());

        rs.close();

        connection.close();

      }&nbspcatch (SQLException&nbspex) {

        throw&nbspnew&nbspSAXException(ex);

      }

    }

    public&nbspvoid&nbspparse(ResultSet&nbsprs,&nbspString&nbsptableName)

    throws&nbspSAXException,&nbspSQLException,&nbspIOException {

      if (_documentHandler ==&nbspnull) {

        return; //&nbspnobody&nbspis&nbspinterested& nbspin&nbspme,&nbspno&nbspneed&nbspto&nbspsweat!

      }

      ResultSetMetaData&nbsprsmd =&nbsprs.getMetaData();

      int&nbspnumCols =&nbsprsmd.getColumnCount();

      String&nbsptableMarker =&nbspgetTableMarker(tableName);

      String&nbsprowMarker =&nbspgetRowMarker();

      _documentHandler.startdocument();

      _documentHandler.startElement(tableMarker,&nbsp_stockEmptyAttributeList);

      while(rs.next()) {

        _documentHandler.startElement(rowMarker,

        _stockEmptyAttributeList);

        for (int&nbspi =&nbsp1;&nbspi <=&nbspnumCols;&nbspi++) {

          generateSAXEventForColumn(rsmd,&nbsprs,&nbspi);

        }

        _documentHandler.endElement(rowMarker);

      }

      _documentHandler.endElement(tableMarker);

      _documentHandler.enddocument();

    }

    public&nbspvoid&nbspparse(String&nbspconnectionURL,& nbspString&nbspuserName,&nbspString&nbsppasswd,

    String&nbsptableName)&nbspthrows&nbspSAXException,&nbspIOException {

      parse(new&nbspJDBCInputSource(connectionURL,&nbspuserName,&nbsppasswd,&nbsptableName));

    }

    //------------------------------------------------------------------

    //&nbspProtected&nbspmethods&nbspthat&nbspderived&nbspclasses&nbspcould&nbspoverride&nbspto

    //&nbspcustomize&nbspthe&nbspparsing.

    //------------------------------------------------------------------

    protected&nbspvoid&nbspgenerateSAXEventForColumn(ResultSetMetaData&nbsprsmd,

    ResultSet&nbsprs,

    int&nbspcolumnIndex)

    throws&nbspSAXException,&nbspSQLException {

      String&nbspcolumnvalue =&nbsprs.getString(columnIndex);

      if (columnvalue ==&nbspnull) {

        return;

      }

      String&nbspcolumnMarker =&nbspgetColumnMarker(rsmd.getColumnLabel(columnIndex));

      char[]&nbspcolumnvalueChars =&nbspcolumnvalue.toCharArray();

      _documentHandler.startElement(columnMarker,

      _stockEmptyAttributeList);

      _documentHandler.characters(columnvalueChars,0,&nbspcolumnvalueChars.length);

      _documentHandler.endElement(columnMarker);

    }

    protected&nbspString&nbspgetTableMarker(String&nbsptableName) {

      return&nbsptableName;

    }

    protected&nbspString&nbspgetRowMarker() {

      return "row";

    }

    protected&nbspString&nbspgetColumnMarker(String&nbspcolumnName) {

      return&nbspcolumnName;

    }

    protected&nbspString&nbspgetSelectorSQLStatement(String&nbsptableName) {

      return "select *&nbspfrom " +&nbsptableName;

    }

  }

 让我们来看看上述代码的详细内容。JDBCSAXParser 包括了几个重载的&nbspparse() 方法。在下表中 org.xml.sax.Parser 接口需要实现&nbspparse(InputSource) 与&nbspparse (String) 方法。其它&nbspparse() 方法简化了代码并允许通过派生类重载以改变其解析方法。

  如果参数为&nbspJDBCInputSource 类型,Parse(InputSource) 方法调用&nbspparse (JDBCInputSource) 方法,否则,产生一个&nbspSAXException 事件表示无法处理数据源。当所提供的信息不足以访问数据库时&nbspparse(String) 方法产生一个&nbspSAXException 事件。

  Parse(JDBCInputSource) 方法对输入源建立一个连接对象,并执行一个查询以获得一个&nbspResultSet 对象。然后对该对象可调用&nbspparse(ResultSet) 方法。

  Parse(ResultSet,String) 方法执行解析的核心逻辑。它遍历结果集中的每一行与字段。在对每一行循环时调用方法& nbspStartElement() 与&nbspendElement() (将数据库表标识作为元素名参数)。同样的,在对每一行记录的每个字段循环时会调用方法&nbspStartElement() 与&nbspendElement()(将行标识作为元素名参数)。在上述两种情况中一个空的属性表作为第二参数传递给&nbspstartElement()。在访问记录的每个字段时,方法 generateSAXEventForColumn() 被调用(使用字段名与字段值为参数)。通过对结果集对象使用& nbspgetString90 方法可获得单个字段的值,同样我们需要用一个字符串表征字段数据,并在&nbspcharacters() 事件中使用。

  方法&nbspparse(String,&nbspString,&nbspString, String) 通过传递给它的参数简单有效地建立了一个JDBCInputSource 对象,然后可对该对象使用&nbspparse (JDBCInputSource) 方法。

  方法&nbspJDBCSAXParser(protected 方式)通过过载提供了一些专门的特性:

  方法&nbspgenerateSAXEventForColumn() 为字段数据产生事件。一个数据库中的字段空值(null)与一个字段零值(empty)有着不同的含义。我们通过过滤那些具有&nbspnull 值的字段来捕获这种差别。另一种表现数据库中null值的方法是使用一个二进制属性(如&nbspisNull)。通过该选项,一个真值(true)被认为是&nbspnull 值,否则就不是。

  GetTableMarker(),getRowMarker(),及&nbspgetClumnMarker() 方法可分别返回合适的表、行、字段默认值。派生出来的类可重载这些方法以提供特定的标识。

  方法&nbspGetTableMarker() 返回一个“select *&nbspfrom< tableName>”字符串。派生出来的类可通过重载该方法以提供一个不同的&nbspselect&nbspquery 字符串,并实现数据库级的过滤。

  类&nbspJDBCSAXUtils 提供两种方法来建立一个& nbspJDBCInputSource 对象:通过属性(property)文件或一个Property 对象。它不需要通过& nbspSAX 或&nbspDOM 编程接口提供一个有关数据库的参数表给应用程序。它希望用户来提供一个包含完整数据库& nbspURL 入口的属性文件,一个可连接到数据库的用户名及密码,一个用于建立连接JDBC 数据库引擎,及数据库表名。以下是一个典型的属性文件:  

  #&nbspportfolio.prop

  #&nbspJDBCSAXSource&nbspproperty&nbspfile

  URL=jdbc:odbc:db1

  user=jw

  password=jw-passwd

  table=portfolio

  driver=sun.jdbc.odbc.JdbcOdbcDriver

  我们现在有了一个简单的解析器,它能对数据库表产生适当的SAX事件。它能区分&nbspnull 值并提供一些专用的标识。这些功能对于一些应用已经足够了,而某些完整的解决方案还需要一些附加的功能,这是因为:

  解析器不能合并那些关联的信息,该问题的解决可通过使用&nbspXpointer/Xlink 来设置表中外键的相关信息。

  数据库中的一个text字段可能会包含标识(marked-up)信息。一个用于数据库的&nbspSAX 解析器应该能解析这类数据并产生适当的SAX事件。如果这类功能对某个应用非常重要,可以通过重载generateSAXEventForColumn() 方法并解析该字段的内容以产生附加的&nbspSAX 事件

  在数据库中,一个数据库表包含了未排序的字段集;有关字段存储的排序并不重要。另一方面,一个&nbspXML&nbspDTD,无法描述一个未排序的子元素集。

  我们可以通过几种方法处理该问题。如果我们要将数据库转化为另一种&nbspXML 文档,比如说一个&nbspHTML 页面,为其定制的&nbspXSLT 样式表可建立正确排序的输出结果。我们也可以重载&nbspgetSelectorSQLStatement () 方法直接提供一个正确排序的字段列表。

  有时我们希望通过某些查询能将一个表的被选择部分作为一个&nbspXML  文档封装。如果&nbspXML 工具能实现这样的过滤,我们就可以更好的使用数据库。方法& nbspgetSelectorSQLStatement() 可以通过重载并返回合适的select 结果字符串。

  解析器通过对结果集(result-set)对象使用&nbspgetString() 方法可获得字符串形式的某个字段值。该操作对于文本、数字等类型的字段非常合适,但对于二进制数据就不适合。当使用文本表示二进制数据时,在某些操作中会无法使用。解析器同样无法处理某些可被& nbspSQL3/JDBC2.0 接受的用户自定义的数据类型。

  对于上述问题我们可以通过重载&nbspgenerateSAXEventForCloumn() 方法以及提供一种合适的处理(implementation)来实现。

  面向数据库的&nbspDOM 编程接口实现

  为了建立一个对应于一个数据库表的&nbspDOM 树,我们可以遍历每一行每个字段并为其建立树结点,或者我们可以通过其它的类库,如 &nbspSun 的&nbspJAXP 工具,它可以通过一个&nbspSAX 事件流建立一个&nbspDOM  树。后一个方法更简单,代码更简练,因为它利用了一个已有的功能。为了通过这种方法实现&nbspDOM 编程接口,我们需要一个合适的 &nbspSAX 数据库解析器,使我们的实现更方便。

  将DOM数据库编程接口与&nbspXQL 处理器相集成

  通过类&nbspJDBCDOMParser 实现面向数据库的&nbspDOM 编程接口:

  //&nbspJDBCDOMParser.java

  package&nbspdbxml.dom;

  import&nbspjava.io.IOException;

  import&nbsporg.w3c.dom.document;

  import&nbsporg.xml.sax.SAXException;

  import&nbspcom.sun.xml.tree.XmldocumentBuilder;

  import&nbspdbxml.sax.*;

  public&nbspclass&nbspJDBCDOMParser {

    public&nbspstatic document&nbspcreatedocument(JDBCInputSource&nbspinputSource) throws&nbspSAXException,&nbspIOException {

      XmldocumentBuilder documentBuilder =&nbspnew&nbspXmldocumentBuilder();

      JDBCSAXParser&nbspsaxParser =&nbspnew&nbspJDBCSAXParser();

      documentBuilder.setParser(saxParser);

      saxParser.parse(inputSource);

      return documentBuilder.getdocument();

    }

  }

  有关类&nbspJDBCDOMParser 的实现较为简单,它通过&nbspJAXP 所提供的&nbspXmldocumentBuilder 类可以从一个SAX事件流构建一个&nbspDOM 文档。JDBCDOMParser 仅有一个方法:createdocument(),它需要一个&nbspJDBC 数据源作为参数。该方法建立一个&nbspJDBCSAXParser 并可用其来解析一个实际的&nbspXmldocumentBuilder 对象。然后它释放解析对象并返回&nbspXmldocumentBuilder 对象中产生的结果。在实际编程实现中,XmldocumentBuilder 对象通过建立一个&nbspDOM 文档的方法来响应&nbspJDBCSAXParser 对象所产生的SAX事件。

  利用面向数据库的&nbspSAX 编程接口

  我们已经看了一个通过面向数据库的SAX编程接口来实现&nbspDOM 编程接口的实例。现在我们要看另一个使用SAX 编程接口的例子。在本节中,我们将看到如何将&nbspSAX 数据库编程接口与XT(一个用&nbspJava 写的&nbspXSLT 处理器)相集成。通过这种集成,我们可以对一个存储在数据库中的虚拟XML文档直接使用&nbspXSLT 样式表。

  我们封装了实际建立一个&nbspSAX 数据库源的逻辑,并用给定的XSLT样式表加以处理,在类JDBCXSLProcessor 中产生一个输出文件(使用&nbspXT 中的&nbspcom.jclark.xsl.sax.Driver)。其中主要的方法包含三个参数:一个数据库特征文件,一个&nbspXSLT 样式表文件,一个输出文件。

  如我们即将在下面看到的,我们可以通过这种方法直接生成 &nbspHTML 页面而不需要一个临时的&nbspXML 文件来过渡。另外,我们还将看到如何将&nbspSAX 数据库编程接口与XT相结合,以将一个数据库转化为一个物理的&nbspXML 文档。

  利用&nbspXSLT 样式表从数据库直接生成&nbspHTML 页面

  现在我们来看一个简单的样式表,它可以将一个高度规范的&nbspXML 文档(基于数据库表)格式化显示。该数据库表将被格式化为一个 &nbspHTML 表。样式表&nbspcreateTable.xsl 可以被用于处理任何具有表状结构的& nbspXML 文档。该样式表使用字段的标记名作为表头标题。

  通过一个&nbspXSLT 样式表将一个数据库转化为一个&nbspXML 文档

  尽管大多数&nbspXML 应用程序可以直接使用&nbspSAX 或&nbspDOM 编程接口,不过在某些场合我们仍需要得到一个实际的&nbspXML 文档。举例而言,对于某个不使用任何一种&nbspXML&nbspAPI的工具,我们就需要一个物理&nbspXML 文档。在这里,我们推荐一种由数据库生成&nbspXML 文档的方法。在这种方法中,我们制定一个 &nbspXSLT 样式表来实现统一的转换。使用这样一个样式表(与一个&nbspSAX 数据库编程接口相结合),我们可以生成一个与数据库表相对应的XML 文档。在此,作者提供了一个样式表&nbspidentity.xsl 可用于在当前版本的&nbspXT  中,以实现数据的统一转换。

  identity.xsl

  <xsl:stylesheet&nbspversion="1.0"

  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:template&nbspmatch="@*|*">

  <xsl:copy>

  <xsl:apply-templates&nbspselect="@*|node()"/>

  </xsl:copy>

  </xsl:template>

  </xsl:stylesheet>

  注意虽然一个&nbspXSLT 样式表可以容易地实现数据的成批转换,不过这种方法并不十分有效,因为一个完整的样式表其逻辑也许会非常复杂,从而影响样式表的建立及实际数据转换的效率。尤其当数据库表中包含大量记录时这个问题会特别明显。一个折衷的方法是写一个专门用于批量转换的应用程序。这样一个应用程序可以监听&nbspSAX 事件并建立&nbspXML 元素(及对应的实际数据),所产生的结果& nbspXML 文档与数据库表相对应。

  使用DOM数据库编程接口

  在大多数场合,SAX 数据库编程接口较&nbspDOM 编程接口更节约系统资源。不过,某些应用程序要求随意的访问&nbspXML 文档,因此就会需要提供一个类似与&nbspDOM 的树形结构来表示数据库。

  将&nbspDOM 数据库编程接口与&nbspXQL 处理器相集成

  XML&nbspQuery&nbspLanguage(XQL)是一种用于&nbspXML 文档查找的语言,其语法与 &nbspXpath 方式相近。在这里,我们将我们的&nbspDOM 数据库解析器与&nbspGMD-& nbspIPSI’S&nbspXQL&nbspEngine 相集成。通过这种集成我们可以对一个表征数据库表的& nbspXML 文档执行类似于&nbspSQL 的查询。

  作为一个集成的例子,作者提供一个简单封装的用于查询一个基于数据库表的&nbspXML 文档。

  类&nbspJDBCXQLProcessor 建立一个类似于封装的环境,它接收客户的查询并输出结果文档。方法 PprocessQueries() 可以操作任何文档对象不仅是由&nbspJDBCDOMParser 所建立的对象。它读系统,执行查询请求并输出系统的查询结果。Main() 方法可通过其参数建立一个&nbspJDBCInputSource 对象并将其传递给 JDBCDOMParser 以获得一个与所给数据库表相对应的文档对象。

  另外说明一点,通过& nbspXMLWriter 与JDBCDOMParser 写一个数据库到&nbspXML 的转换采用的是快照方式。即从 JDBCDOMParser 得到一个文档对象并通过&nbspXMLWriter.write(document) 写到目标文件中。

  在本文中,我们讨论了如何通过面向数据库的&nbspXML 编程接口来表征数据库中的信息。通过这种编程接口,我们可以避免将一个数据库转换为一个实际的&nbspXML 文档以及避免保持二者间的同步。我们介绍了一种通过Java 实现的&nbspSAX 与& nbspDOM 数据库编程接口,然后介绍了一种与&nbspXT 相集成的&nbspSAX 编程接口。我们演示了通过这种集成由数据库表直接导出&nbspHTML 页面以及将数据库表转换为&nbspXML 文档。最后,我们将&nbspDOM 数据库编程接口与一个&nbspXQL 处理器相集成。