Online Store | Contact Us | How To Buy

Products
 
Downloads
 
Support
 
Developer
 
OEM Login
 
OEM/ Channel Partner
 
Case Studies
 
Company
 
Developer Home
 
Forums
 
Code Library
 
JDBC
 
ODBC
 
.NET
 
XML
 
Design Preview
 
Bottom
 


SQL/XML in JDBC Applications
The simple way for Java applications to generate XML from SQL queries using the SQL/XML features of SQL 2003

Download .pdf

Introduction

This document explores different approaches for creating XML structures from SQL queries by comparing SQL/XML to custom-coded solutions using JDBC and SAX or DOM and to proprietary XML extensions from the database vendors. Many Java applications exchange data as XML, but store and query data using a JDBC connection to a traditional relational database. Unfortunately, XML and SQL represent information in very different ways, and many developers spend significant effort converting between the two formats. When producing XML for exchange, developers need a way to build hierarchical XML structures using queries on a set of unordered, two-dimensional tables. Similarly, they need a way to update the content of their two dimensional tables using data found in XML hierarchies.

SQL/XML, part of the SQL 2003 standard, is an extension to the SQL standard that allows XML to be generated as the result of a query, and adds an XML data type to SQL so that XML query results can be returned in the columns of normal SQL result sets. Using standard SQL/XML instead of custom-coded solutions or proprietary vendor extensions greatly simplifies development, provides for more maintainable code, and allows portability across databases.

After introducing SQL/XML, we take a simple usage scenario and show how a Java program might generate XML from a relational database. We provide code examples to compare the following solutions:

  • JDBC + SQL/XML
  • JDBC + DOM + SQL
  • IBM DB2 UDB
  • Oracle XSU
  • Microsoft SQL Server

We show that the first two solutions in the preceding list are portable across operating systems and database vendors, but the SQL/XML solution requires much less code, is more efficient, and is easier to write and maintain than building XML using DOM and SQL queries. We show that the last three solutions, which rely on proprietary extensions from the database vendors, are not only non-portable, but are also harder to learn and more complex to use than Java applications that use SQL/XML and a JDBC connection.


Table of Contents

SQL/XML: SQL Queries that Create XML

Mapping a SQL Tableto an XML Document

SQL/XML Functions

Using SQL/XML with JDBC

DataDirect Connect for SQL/XML

SQL/XML, DOM+JDBC, and Proprietary Database Extensions

Usage Scenario: Customers and Projects

SQL/XML Example

Custom-Coding

DB2 UDB

Oracle

Microsoft SQL Server

Conclusion

References


SQL/XML: SQL Queries that Create XML
In many environments, the vast majority of important data is stored in relational databases, but the majority of data exchange is done using XML, which is used to provide data for exchange as web messages or for display on web sites. Making relational data ready for data exchange typically involves a significant amount of work, because data in relational databases is represented as distinct two-dimensional tables that must be joined together to create different representations of the same information. Data in XML is represented using only hierarchy and sequence - two relationships that do not exist in traditional relational databases. The developer is forced into the role of restructuring and translating between two very different worlds. Because the same information may be restructured in a variety of ways to create many different XML documents, developers often spend a great deal of time performing this one task.

For example, consider the following relational tables:

So that the information in these tables can easily be displayed on a web site or exchanged in web messages, we need to convert it to XML. In most applications, the XML that is displayed or exchanged is the result of some query.

For example, here's an XML document that lists the projects for each customer - it was created by joining these two tables and creating a hierarchical XML structure to contain the following result:

<?xml version="1.0" encoding="UTF-8"?>
<customers>
  <customer id="1">
    <name>Woodworks</name><city>Baltimore</city>
    <projects>
      <project id="1"><name>Medusa</name></project>
    </projects>
  </customer>
... 
  <customer id="4">
    <name>Hardware Heaven</name><city>Washington</city>
    <projects>
      <project id="2"><name>Pegasus</name></project>
      <project id="8"><name>Typhoon</name></project>
    </projects>
  </customer>
...
</customers>

Applications typically use SQL queries to generate the information needed to build an XML document. Most applications devote significant effort to converting between relational data and XML documents because of their different representations. Because the information from a database can be displayed in many different ways on web pages or in web messages, many companies want a standard and efficient way to create XML as the result of SQL queries.

The first popular commercial solutions to this problem came from the relational database vendors, for whom XML support has been an important competitive feature. Almost all major RDBMS vendors now supply tools or product enhancements to help bridge the gap between XML and relational data. Building on their experience, the database vendors initiated an action within the ANSI/ISO SQL standard to provide XML support as part of SQL 2003. SQL/XML [8] provides XML functions that allow SQL queries to create XML structures and specifies how SQL tables, schemas, catalogs, data types, values, character sets, and names are translated to and from XML. SQL/XML also introduces a new data type, the XML data type.

In the first half of this document, we present SQL/XML's XML mapping and the five XML functions used to create XML structures. The second half of this paper shows a SQL/XML query and the code needed to implement the same SQL99 query together with the DOM, and with the proprietary XML extensions for Microsoft SQL Server, IBM DB2, and Oracle. We will show that using standard SQL/XML instead of proprietary vendor extensions greatly simplifies development, provides for more maintainable code, and allows portability across databases.

Mapping a SQL Table to an XML Document

SQL/XML queries create XML from relational data, and the standard specifies exactly how SQL tables, schemas, catalogs, data types, values, character sets, and names are translated to and from XML. This specification is quite detailed, but the results are intuitive and easily grasped by looking at an example. Consider the Customers table:

Here's an XML document that corresponds to the preceding table using the default SQL/XML mappings:

<Customers>
   <row> 
      <CustId>1</CustId>
      <Name>Woodworks</Name>
      <City>Baltimore</City>
   </row>
   <row> 
      <CustId>2</CustId>
      <Name>Software Solutions</Name>
      <City>Boston</City>
   </row>
   <row> 
      <CustId>3</CustId>
      <Name>Food Fantasies</Name>
      <City>New York</City>
   </row>
  ...
</Customers>

SQL/XML Functions

You can use SQL/XML functions to create XML results in the SELECT clause of a SQL statement.

XMLELEMENT - XMLATTRIBUTES

The XMLELEMENT and XMLATTRIBUTES functions are used to create XML elements and XML attributes. The following example demonstrates how to create a hierarchical XML structure with XML elements and attributes based on data from the Customers table:

SELECT
   c.CustId,
   XMLELEMENT(NAME "customer",
   XMLATTRIBUTES(c.CustId AS "id"),
      XMLELEMENT(NAME "name",c.Name),
      XMLELEMENT(NAME "city",c.City)) AS "CustInfo"
FROM Customers c

The result set that is created contains two columns: the first column contains an integer value (CustId), the second column contains the result of the SQL/XML expression, which is a value whose data type is XML.

XMLFOREST

The XMLFOREST function creates a list of XML values from a list of SQL values using SQL/XML mappings. Here's a query that uses XMLFOREST to create XML data:

SELECT XMLFOREST(c.CustId AS "id",c.Name,c.City)
FROM Customers c

Here's the result of the preceding query:

XMLFOREST is simple to use because it uses default mappings and does not force the user to specify how XML structures are created; however, it does not allow the user as much choice when compared to the functions that explicitly create XML structures, such as XMLELEMENT. It can be combined with other functions, such as XMLELEMENT, and elements can be renamed by using the SQL "AS" clause to rename a SQL column before the mapping is applied, as shown in the following query:

SELECT
   c.CustId,
   XMLELEMENT(NAME "customer",
      XMLFOREST(c.CustId AS "id", 
                c.Name AS "name", 
                c.City AS "city")
              )
FROM Customers c
Here's the result of the preceding query:

XMLAGG

The XMLAGG function often is used to create child elements within a parent element using a nested query. It concatenates all XML values that are returned from a collection, such as a nested query, and returns a single XML value. For example, in the following query, the parameter for the last XMLELEMENT function must be a single XML value:

SELECT
   c.CustId,
   XMLELEMENT(NAME "customer",
      XMLATTRIBUTES(c.CustId AS "id"),
      XMLELEMENT(NAME "name",c.Name),
      XMLELEMENT(NAME "city",c.City),
      XMLELEMENT(NAME "projects",
      (SELECT XMLAGG(XMLELEMENT(NAME "project",
                 XMLATTRIBUTES(p.ProjId AS "id"),
                 XMLELEMENT(NAME "name",p.Name)))
      FROM Projects p 
      WHERE p.CustId=c.CustId))) AS "customer-projects"
FROM Customers c
In this example, the XMLAGG function concatenates (aggregates) all project information from the nested query into one XML value, which lists all the projects for a given customer. This XML value becomes the content of the element named projects.

XMLCONCAT

In SQL, the distinction between a single value and a list of values is meaningful, so SQL/XML must be able to distinguish a single XML value with multiple XML elements from a list of XML values that each contain one XML element. For example, in a SELECT clause, each value of a tuple is represented in a column of the result.

Here's a query that creates a list of XML values in the SELECT clause:

SELECT
   c.CustId,
   XMLELEMENT(NAME "id",c.CustId),
   XMLELEMENT(NAME "name",c.Name),
   XMLELEMENT(NAME "city",c.City) 
FROM Customers c
Here's the result of the preceding query:

The XMLCONCAT function allows the three XML elements to be concatenated into one value, so that they all appear in the same column. Here's a query that concatenates the three XML elements into a column named CustInfo:

SELECT
   c.CustId,
   XMLCONCAT(
      XMLELEMENT(NAME "id",c.CustId),
      XMLELEMENT(NAME "name",c.Name),
      XMLELEMENT(NAME "city",c.City)) AS "CustInfo"
FROM Customers c
In the following result set, you can see that the second column, CustInfo, contains the concatenation of the three XMLELEMENT expressions:

Using SQL/XML with JDBC

Using SQL/XML with JDBC is similar to using SQL with JDBC, except that the result set can contain instances of the XML data type. JDBC 3.0 does not support the XML data type; it is expected that JDBC 4.0 will support it. In the meantime, JDBC-based implementations that allow SQL/XML queries must provide their own XML data type to retrieve XML results.

The following JDBC example retrieves a result set using a SQL/XML query and extracts the XML values from the result set:

...
String sqlStr=
   new String ("SELECT c.CustId,xmlelement(NAME ....\n"));
Statement stat=con.createStatement();
ResultSet rs=stat.executeQuery(sqlStr);
while(rs.next()) 
   {
   int id=rs.getInt(1);
   com.ddtek.jdbc.jxtr.XMLType xmlC=
      (com.ddtek.jdbc.jxtr.XMLType)rs.getObject(2);
   org.w3c.dom.Document doc=xmlC.getDOM();
   ...
   }
...
DataDirect Connect for SQL/XML

DataDirect Connect for SQL/XML is a cross-platform implementation of SQL/XML. It lets Java applications return XML values in the columns of JDBC result sets and access XML columns as JDOM, SAX, DOM, or text. It allows you to write applications that work without change for any major database including Oracle, DB2, SQL Server, Informix, and Sybase.

Currently, the SQL/XML standard supports queries, but not updates. DataDirect Technologies is a member of the H2.3 Task Group, which is responsible for SQL/XML, and we hope that updates will be added to the standard. In the meantime, Connect for SQL/XML provides proprietary extensions to the SQL99 Insert, Update, and Delete statements that allow you to update data stored in relational databases with information extracted from XML.

Connect for SQL/XML makes it easy for you to integrate SQL/XML into your environment. It includes a graphical Query Builder that allows developers to quickly create, modify, and test SQL/XML queries without having to know the details of SQL/XML. In addition, it is shipped with examples that show how you can use SQL/XML queries to achieve different results.


SQL/XML, DOM+JDBC, and Proprietary Database Extensions
Now that we have introduced SQL/XML, let us compare SQL/XML to the other solutions available to SQL developers today. We use the same simple problem posed in the first section of this document, show a SQL/XML query that solves it, and present the code based on several alternative solutions.

Usage Scenario: Customers and Projects

Our scenario includes data extracted from two different relational tables, a Customers table and a Projects table, to create an XML document that lists the projects for each customer:

The "Customers" table contains a customer identifier, customer name, and customer address. The "Projects" table contains a project identifier, project name, and customer identifier.

Here's the desired XML output for a single customer:

<customer id="4">
    <name>Hardware Heaven</name><city>Washington</city>
    <projects>
      <project id="2"><name>Pegasus</name></project>
      <project id="8"><name>Typhoon</name></project>
    </projects>
  </customer>

SQL/XML Example

The solution to our problem of generating XML structures from relational data is straightforward using SQL/XML. In the following solution, we use the SQL/XML XMLAGG function to build the list of projects associated with a given customer:

SELECT
   c.CustId,
   XMLELEMENT(NAME "customer",
      XMLATTRIBUTES(c.CustId AS "id"),
      XMLELEMENT(NAME "name",c.Name),
      XMLELEMENT(NAME "city",c.City),
      XMLELEMENT(NAME "projects",
      (SELECT XMLAGG(XMLELEMENT(NAME "project",
                 XMLATTRIBUTES(p.ProjId AS "id"),
                 XMLELEMENT(NAME "name",p.Name)))
      FROM Projects p 
      WHERE p.CustId=c.CustId))) AS "customer-projects"
FROM Customers c

The XML values are returned in the columns of a normal SQL result set as shown:

Using JDBC and Connect for SQL/XML, we can extract these XML values programmatically from the result set to construct an XML document as shown in the following code:

// Create the Connect for SQL/XML JDBC connection
connectWithConnectForSQLXML();

// Build SQL/XML query
StringBuffer sqlXml = new StringBuffer();
sqlXml.append ( " SELECT "); 
sqlXml.append ( " c.CustId, ");
sqlXml.append ( " XMLELEMENT (NAME \"customer\", ");
sqlXml.append ( " XMLATTRIBUTES(c.CustId AS \"id\"), ");
sqlXml.append ( " XMLELEMENT(NAME \"name\", c.Name), ");
sqlXml.append ( " XMLELEMENT(NAME \"city\", c.City)), ");
sqlXml.append ( " XMLELEMENT(NAME \"projects\", " );
sqlXml.append ( " (SELECT XMLAGG(XMLELEMENT(NAME \"project\", ");
sqlXml.append ( "            XMLATTRIBUTES(p.ProjId AS \"id\"), ");
sqlXml.append ( "            XMLELEMENT(NAME \"name\",p.Name))) ");
sqlXml.append ( " FROM Projects p ");
 
sqlXml.append ( " WHERE p.CustId=c.CustId))) AS \"customer-projects" ");
sqlXml.append ( " FROM Customers c ");

// Output result header
System.out.println( "---------------------------------------" );
System.out.println( "Query result (via getString on XMLType)" );
System.out.println( "---------------------------------------" );

// Execute the SQL/XML query
resultSet = stmt.executeQuery(sqlXml.toString());

// Retrieve data from the resultset
while ( resultSet.next() )
   {
   XMLType xmlType = (XMLType)resultSet.getObject(1);
   System.out.println(xmlType.getString());
   }

resultSet.close();

The SQL/XML query that produces the desired result can be easily maintained outside the application within a properties file or database, for example. Because SQL/XML is standards-based, this same query will work across a variety of databases.

Although the SQL/XML standard currently supports queries and not updates, DataDirect Connect for SQL/XML provides proprietary extensions to the SQL99 Insert, Update, and Delete statements that allow you to update data stored in relational databases with information extracted from an XML document. You specify XPath expressions to extract the relevant subset of information from the XML document. For example, let's assume that a new project named Gryphon is added for the customer Hardware Heaven and the resulting XML document looks like this:

<?xml version="1.0" encoding="UTF-8"?>
<customers>
    <customer id="4">
    <name>Hardware Heaven</name><city>Washington</city>
    <projects>
      <project id="2"><name>Pegasus</name></project>
      <project id="8"><name>Typhoon</name></project>
    <project id="11"><name>Gryphon</name></project>
    </projects>
  </customer>
</customers>
To update the Projects database in our example with the new project named Gryphon, we can insert a new row in the Projects table using the proprietary extension for the SQL99 Insert statement provided by Connect for SQL/XML. Our code would look like this:
INSERT xml_document('c:/temp/newprojects.xml')
   into Projects (ProjId, Name, CustId)
   xml_row_pattern('/customers/customer/projects/project')
   values(xml_xpath('@id','Integer'),
          xml_xpath('@name/text()'),
          xml_xpath('../../@id','Integer')
          )
For more information about XPath, refer to http://www.w3.org/TR/xpath.

Custom-Coding

Custom-coding a solution to solve our problem is an alternative approach. APIs that can be used to access the database (for example, JDBC) have been available for some time. More recently, XML APIs (primarily, DOM and SAX) have been added to the developer's arsenal of available tools. Why not custom-code a solution that directly accesses the database and passes the information to one of the XML APIs?

Two issues exist with this approach. The first issue is maintainability. If you examine the custom-coded example that follows, you can see that a substantial amount of code is used to implement a simple conversion. Business requirements tend to change over time, so it is realistic to assume that the requirements for the document structure used to create the XML will probably change. With all this code, adding a new element from another RDBMS column to the document structure, for example, can be time-consuming.

The second issue is application performance. Typically, the queries executed on the database correspond to the structure of the XML document being created. The first query selects top-level elements, and for each nested structure, a new database query is used. This is not the fastest approach available, particularly for heavily nested documents [1].

Here's a code excerpt that uses JDBC to retrieve the data to be converted from the database. Code is written to retrieve data from each relational table, then, additional code is devoted to joining the data to create a usable XML document.

// Open JDBC Connection
String url="jdbc:datadirect:sqlserver://localhost:1433;databaseName=JXTR";
Class.forName("com.ddtek.jdbc.sqlserver.SQLServerDriver");
con=DriverManager.getConnection(url,"demo","secret");

// Create XML document
DocumentBuilderFactory dbf=DocumentBuilderFactory.newInstance();
DocumentBuilder db=dbf.newDocumentBuilder();
doc=db.newDocument();

// Create the root element
Element root=doc.createElement("customers");
doc.appendChild(root);

// Create statement and result set for customer information
Statement stmt1=con.createStatement();
ResultSet rs1=stmt1.executeQuery(
  "select c.CustId,c.Name,c.City from Customers c");

// For all selected customers
while(rs1.next()) {
  // Create customer container element
  Element customer=doc.createElement("customer");
  root.appendChild(customer);
  // "id" attribute and retrieve contents from result set
  String custId=rs1.getString(1); 
  customer.setAttribute("id",custId);
  // "name" child element and retrieve contents from result set
  Element custName=doc.createElement("name");
  custName.appendChild(doc.createTextNode(rs1.getString(2)));
  customer.appendChild(custName);
  // "city" child element and retrieve contents from result set 
  Element custAddress=doc.createElement("city");
  custAddress.appendChild(doc.createTextNode(rs1.getString(3)));
  customer.appendChild(custAddress);
  // "projects" child container element
  Element projects=doc.createElement("projects");
  customer.appendChild(projects);

  // Create statement/result set for project information
  Statement stmt2=con.createStatement();
  ResultSet rs2=stmt2.executeQuery(
    "select p.ProjId, p.Name from Projects p where p.CustId="+custId);

// For all selected projects
  while(rs2.next()) {
    // Create project container element
    Element project=doc.createElement("project");
    projects.appendChild(project);
    // "id" attribute and retrieve contents from result set
    project.setAttribute("id", rs2.getString(1));
    // "name" child element and retrieve contents from result set
    Element projName=doc.createElement("name");
    projName.appendChild(doc.createTextNode(rs2.getString(2)));
    project.appendChild(projName);
    }
  }

// 'Print' DOM document (Xerces version)
OutputFormat of=new OutputFormat();
of.setIndent(2);
Writer ow=(Writer)new OutputStreamWriter(System.out);

XMLSerializer xmlS=new XMLSerializer(ow,of);
xmlS.serialize(doc);

DB2 UDB

For some time, DB2 has been shipping an XML integration tool, DB2 XML Extender [2]. With version 8.1, DB2 also supports SQL/XML [3].

DB2 XML Extender, like all DB2 extenders, works as user-defined data types (UDTs) and associated user-defined functions (UDFs) that implement operations on these UDTs. DB2 XML Extender defines three UDTs: XMLVarchar, XMLCLOB, and XMLFILE. These data types are used to store XML information in its native format, and the operations defined on these UDTs manipulate the XML information.

DB2 XML Extender supports two major modes of operation: XML column mode and XML collection mode.

XML Column Mode

In XML column mode, complete XML documents are stored in a single database column (using one of the previously mentioned XML UDTs). Furthermore, parts of the XML document can be stored in normal RDBMS (indexed) table columns that are linked to the original XML document for quick lookup. Although this is certainly a useful feature, it does not solve the problem of moving data to and from the relational data model and the XML data model.

XML Collection Mode

XML collection mode supports both composing and writing XML information from or into DB2 table columns. The XML collection mode has the following characteristics:

  • A Document Access Definition file (DAD) describes the mapping between the data persisted in table columns and information stored in XML elements or attributes.
  • A DAD file is an XML document that supports two different mapping schemes: RDB_node and SQL mapping. SQL mapping-based DAD files can only be used to compose XML documents (not to de-compose them).
  • When using RDB_node mapping, references to one or more tables and columns are inserted in the DAD file to specify which table columns map to which XML element or attribute.
  • When using SQL mapping, a single SQL SELECT statement is used to define the content of the XML document. The column names returned from the SELECT statement are used to specify the link to the XML information (elements/attributes).
  • DAD files can be passed as "string" objects to the DB2 XML Extender UDFs. Alternatively, DB2 XML Extender allows you store DAD files in a catalog in the database and reference them by name.
  • Composed XML is inserted in a database table using an available XML UDT. To retrieve the XML in your application, you use a normal SELECT statement.

The following contents of a DAD file implements the mapping for our example. Looking at the contents of this DAD file, you can see that it describes the structure of the XML document to create or consume the XML using element_node and attribute_node elements and that the mapping between the structure and the relational information is provided by using the RDB_node elements.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE DAD SYSTEM "dad.dtd">
<DAD>
  <validation>YES</validation>
  <Xcollection>
    <prolog>?xml version="1.0"?</prolog>
    <doctype>!DOCTYPE customers SYSTEM "customers.dtd"</doctype>
    <root_node>
      <element_node name="customers">
        <element_node name="customer">
          <RDB_node>
            <table name="Customers" key="CustId" />
            <table name="Projects" key="ProjId" />
            <condition>
              Customers.CustId=Projects.CustId
            </condition>
            </RDB_node>
          <attribute_node name="id">
            <RDB_node>
              <table name="Customers" />
              <column name="CustId" type="integer"/>
            </RDB_node>
           <RDB_node>
              <table name="Projects" />
              <column name="CustId" type="integer"/>
            </RDB_node>

          </attribute_node>
          <element_node name="name" >
            <text_node>
              <RDB_node>
                <table name="Customers" />
                <column name="Name" type="varchar(50)"/>
              </RDB_node>
            </text_node>
          </element_node>
          <element_node name="city" >
            <text_node>
              <RDB_node>
                <table name="Customers" />
                <column name=“city” type="varchar(50)"/>
              </RDB_node>
            </text_node>
          </element_node>
          <element_node name="projects">
            <element_node name="project">
              <attribute_node name="id">
                <RDB_node>
                  <table name="Projects" />
                  <column name="ProjId" type="integer"/>
                </RDB_node>
              </attribute_node>
              <element_node name="name" >
                <text_node>
                  <RDB_node>
                    <table name="Projects" />
                    <column name="Name" type="varchar(50)"/>
                  </RDB_node>
                </text_node>
              </element_node>
            </element_node>
          </element_node>
        </element_node>
      </element_node>
    </root_node>
  </Xcollection>
</DAD>
The DAD file can be used to either retrieve the relational data from the database into XML or insert the XML into the database using the stored procedures ddGenXML (to retrieve the relational data and convert it to XML) and dxxShredXML (to insert the XML into the database).

Oracle

Similar to DB2 UDB, Oracle has been shipping XML integration tools for some time. Prior to Oracle 9iR2, most of these integration tools were bundled in a separately downloadable package (Oracle XDK). With release 9iR2, Oracle positions its database server also as an XML database server. The Oracle 9iR2 release adds a number of XML-related features, including SQL/XML support.

From the complete set of XML related modules and features available in Oracle 9iR2, XML SQL Utility (XSU) [4] and SQL/XML [5] support only are relevant for the information mapping problem we are addressing in this document.

XML SQL Utility

XSU is packaged as a set of Java classes. A wrapper PL/SQL package (DBMS_XMLGEN) and a flexible command-line utility are provided as well.

From Relational to XML

XSU can render the result of any SQL query into a fixed-format XML structure. The mapping from query result to XML format takes into account the features of the Oracle (object) type system and, in combination with object views defined on top of non-object typed data, a flexible mapping can be created.

The rules that determine the XML structure derived from the query result include:

  • By default, a <ROWSET> container element is created. Optionally, the element name can be changed or the element can be omitted.
  • For each row retrieved, a <ROW> container element is created with an ID attribute that contains an increasing cardinal number. Optionally, the element name can be changed; the ID attribute or the complete <ROW> element can be omitted.
  • For each column in the retrieved rows, a sub-element is created. Its name is derived from the result set column name. Its content is the value that is retrieved from the database.
    • When the name derived from the query result starts with a ‘@’, an attribute is created instead of an element.
    • When the retrieved data is an instance of an Oracle object type, a container element is created. Its name is derived from the column name. For each attribute of the object type, an element is created. Its name is derived from the object attribute name. The content is set to the attribute value retrieved from the database.
    • When the retrieved column data is an instance of an Oracle collection type, a container element for the collection is created. Its name is derived from the column name. For each item in the collection, a container element is added. Its name is derived from the column name and extended with an _ITEM suffix. Finally, beneath this container element, elements are created with the actual data. The names of these elements are derived from the names of the attributes of the collection type.

From XML to Relational

The inverse operation (inserting, updating, or deleting relational data based on XML information), is slightly less flexible. For example:

  • Attributes are ignored.
  • To create the requested mapping, you often must create object views on top of more traditional relational data. These views are not always updateable. A work-around based on database triggers is possible, but clearly is less elegant.

Using XSU

A set of Java classes (and a PL/SQL package) is provided to execute XSU mappings. A powerful command-line tool exposes most of the options in the API. Finally, the XSQL Servlet is provided to support scalable deployment in Servlet container engines.

XSU Java Classes

The OracleXMLQuery class can be used to execute a query and retrieve the resulting XML. Different methods are available to perform the following tasks:

  • Tune the resulting XML
  • Retrieve the XML in different formats (String, DOM, SAX)
  • Scroll through the XML result
  • Create a DTD or XML Schema based on the query structure

The OracleXMLSave class can be used to insert, update, or delete relational information. The following features are exposed:

  • Set batch size for batched JDBC calls and commit batch size
  • Define a key column list (update and delete)
  • Restrict columns to update
  • Execute the insert, update, or delete statement

XSU Example

The following code excerpt shows the use of the XSU classes. The code extracts the Customer and Project information from the database, and creates the XML document shown in our example.

// Open JDBC Connection
String url="jdbc:oracle:thin:@localhost:1521:ORA92";
Class.forName("oracle.jdbc.driver.OracleDriver");
con=DriverManager.getConnection(url,"jxtr","jxtr" );

// Query
StringBuffer sb=new StringBuffer();
sb.append("SELECT ");
sb.append("  c.custid as \"@id\", ");
sb.append("  c.name, ");
sb.append("  c.City, ");
sb.append("  cursor (  ");
sb.append("    SELECT ");
sb.append("      p.projid as \"@id\", ");
sb.append("      p.name ");
sb.append("    FROM Projects p ");
sb.append("    WHERE p.CustId=c.CustId ) AS projects ");
sb.append(" FROM Customers c ");

// XSU object
OracleXMLQuery q=new OracleXMLQuery(con,sb.toString());

// Result "customization"
q.setRowsetTag("customers");
q.setRowTag("customer");
q.useLowerCaseTagNames();
q.setRowIdAttrName("");

// Stylesheet for result "clean-up"
q.setXSLT("q.xslt",null);

// Generate result document and print
Document resDoc=q.getXMLDOM();
XMLDocument oraDoc=(XMLDocument)resDoc;
oraDoc.print(System.out);
NOTES:
  • The cursor function is used to nest a SELECT statement in the Select list of the parent query.
  • The OracleXMLQuery.setXSLT method was invoked because the XML document created by the default XSU mapping did not match the intended result exactly. XSU does offer flexible XSLT integration. In this specific example, a simple XSLT script (not shown) was needed to create the final XML document.

Microsoft SQL Server

SQL Server 2000, and later, released SQL/XML and Web Services toolkits that expose a series of features that allow you to publish relational data as XML, or populate or update relational information with data retrieved from an XML document. The following sections provide an overview of these features.

Transact-SQL Extensions [6]

  • FOR XML SELECT statement extension - The SELECT syntax is extended with a FOR XML clause that indicates the result of the query must be returned as XML. Different options for returning the XML are available: raw, auto, and explicit. The ease of use of these options range from easy with limited XML mapping ability (FOR XML RAW) to complex with powerful XML mapping features (FOR XML EXPLICIT).

The syntax for the FOR XML extension is:

FOR XML 
  [RAW|AUTO|EXPLICIT]
  [, XMLDATA] 
  [, ELEMENTS]
  [, BINARY BASE64]
  • RAW and AUTO use default (flat) mapping.
  • XMLDATA generates an XDR schema.
  • ELEMENTS creates sub-elements instead of attributes to contain the database data.
  • BINARY BASE64 uses base64 encoding for binary instead of hexadecimal.
  • EXPLICIT uses user-defined mapping.

For details about the different options and parameters, refer to the SQL Server Transact SQL documentation [6].

  • OPENXML statement - OPENXML exposes an XML document as a rowset. The OPENXML row pattern (an XPath expression) defines a set of XML

sub-trees that become the individual rows in the rowset. Individual column values are selected from the XML sub-trees (rows) by specifying one or more column patterns using XPath. Because the result of an OPENXML statement is a rowset, its result can be used as the source for insert, update, or delete statements.

The syntax for the OPENXML extension is:

OPENXML (
  idoc int [in], 
  rowpattern nvarchar[in],
  [flags byte[in]] ) 
[WITH(SchemaDeclaration|TableName)]
NOTE: The idoc parameter contains a handle to a parsed XML document. This handle is created from an XML document by invoking the sp_xml_preparedocument stored procedure.

For details about the different options and parameters, refer to the SQL Server Transact SQL documentation [6].

Web Services Toolkit [7]

  • Client-side XML formatting – ADO and ADO.NET providers are available that support client-side XML formatting for query results. These providers also support the FOR XML server-side XML formatting described previously.
  • XML views – XML Schema files contain annotations that map the XML schema to one or multiple RDBMS tables and associated columns. Once an XML view is defined, XPath expressions can be evaluated against these XML views.
  • DiffGram –XML documents with a fixed schema. The information contained in the XML file lists before and after values that register the changes that need to be applied to an XML view of the RDBMS tables/columns.
  • Templates – Template XML files that contain one or multiple XPath expressions against one or multiple XML views. These template files can also contain SELECT FOR XML statements. Executing the templates combines the result of all these expressions in one XML document.

NOTE: The set of SQL Server features Microsoft identifies as SQLXML is not based on the SQL/XML standard.

Example

The FOR XML extension can be used to create the XML document from the Customer and Project table, and OPENXML can be used to insert the information from the XML document in the database tables.

NOTE: XML views and DiffGram support similar functionality, but they are not easily accessible from within a Java program. The following example is a Java code excerpt that shows how to use the OPENXML function to update columns in the Customers and Projects table with information extracted from the example XML document.

... 
// Build Transact-SQL statement
StringBuffer sb=new StringBuffer();

// Variable declarations
sb.append("DECLARE @idoc int\n");
sb.append("DECLARE @doc varchar(3000)\n");

// Define XML document parameter and parse
sb.append("SET @doc =?\n");
sb.append("EXEC sp_xml_preparedocument @idoc OUTPUT, @doc\n");

// Update Customers from XML
sb.append("update Customers\n");
sb.append("  set Customers.Name=u.Name, Customers.City=u.City\n");
sb.append("FROM OPENXML (@idoc, '/customers/customer',2) \n");
sb.append("WITH  (CustId integer '@id', Name varchar(50) 'name/text()',\n");
sb.append("       Address varchar(50) 'address/text()') u\n");
sb.append("where Customers.CustId=u.CustId\n");

// Update Projects from XML
sb.append("update Projects\n");
sb.append("  set Projects.Name=u.Name\n");
sb.append("FROM OPENXML (@idoc, '/customers/customer/projects/project',2) \n");
sb.append("WITH  (CustId integer '../../@id', ProjId integer '@id',\n ");
sb.append("       Name varchar(50) 'name/text()' ) u\n");
sb.append("where Projects.ProjId=u.ProjId\n");

// Release 'parsed' XML document
sb.append("EXEC sp_xml_removedocument @idoc\n");

// Create statement and set bind marker value
PreparedStatement stmt=con.prepareStatement(sb.toString());
stmt.setString(1,readFile("example.xml"));

// Execute
stmt.execute();
int rowCount;

// Loop through results
do
  {
  rowCount=stmt.getUpdateCount();
  if(rowCount >= 0)
    {
    System.out.println("Updated:"+rowCount);
    stmt.getMoreResults();
  }
  }
while( rowCount>=0 );

con.close();
The following code excerpt creates a Transact SQL statement that is executed using a PreparedStatement.execute method invocation. The Transact SQL requires only one input parameter, the XML document. The value for this input parameter is provided by invoking PreparedStatement.setString.

The Transact SQL statement contains two OPENXML statements. The first extracts Customer information from the input XML document and uses this data to update the Name and Address columns in the Customers table. The second OPENXML statement extracts information from the input XML document and uses this data to update the Name column in the Projects table.

The inverse mapping, creating the XML document based on the RDBMS table data, can be achieved with the following Transact SQL FOR XML statement.

SELECT 
   1 AS Tag,
   0 AS Parent,
   NULL AS [customers!1], 
   NULL AS [jxtr_order_q1!15!!hide], 
   NULL  AS [customer!2!id], 
   NULL  AS [customer!2!name], 
   NULL  AS [projects!3], 
   NULL AS [jxtr_order_q2!16!!hide], 
   NULL  AS [project!4!id], 
   NULL  AS [project!4!name]  
UNION ALL 
   SELECT 2,1, NULL ,1,c.CustId,c.Name, NULL , NULL , NULL , NULL    
   from  Customers   c    
UNION ALL 
   SELECT 3,2, NULL ,2,c.CustId,c.Name, NULL , NULL , NULL , NULL    
   from  Customers   c    
UNION ALL 
   SELECT 4,3, NULL ,3,c.CustId,c.Name, NULL ,1,p.ProjId,p.Name   
   from  Customers   c , Projects   p    
   WHERE  ( p.CustId  =  c.CustId )   
ORDER BY 5,6,7,4,9,10,8,Tag 
FOR XML EXPLICIT

Conclusion
Most database environments use XML, created from data stored in relational databases, to create web messages or to display on web sites. In addition, companies often need to be able to create a variety of XML documents, containing significantly different structures and data. To make this easier and as a competitive feature, almost all relational database vendors have added XML extensions and XML support.

SQL/XML, which is part of SQL 2003, provides a standard and portable way for developers to create any XML structure they need by incorporating XML functions in their SQL queries. For someone who already knows and uses SQL, SQL/XML is easy to learn and is one of the simplest approaches to creating XML from relational data. It is also extremely powerful, allowing the full power of SQL to be combined with the ability to create any desired XML structure from query results. Although there are many other ways to perform the same task, SQL/XML is simple to use, leverages existing SQL knowledge, and makes it possible to write portable applications when used with a JDBC connection.

In this document, we compared SQL/XML to other programming approaches, finding that the SQL/XML solution requires much less code, is more efficient, and is easier to write and maintain than building XML using the DOM and SQL queries. We also found that solutions using proprietary extensions from the database vendors are not only non-portable, but are also harder to learn and are more complex to use than Java applications that use SQL/XML and a JDBC connection.

DataDirect Technologies provides a cross-platform implementation of SQL/XML that allows JDBC programs to access most major databases including Microsoft SQL Server, DB2, Informix, Oracle, and Sybase. It also provides an implementation of the XML data type, which will not be part of the JDBC standard until at least JDBC 4.0, and support for updates, which will not be part of SQL/XML in SQL 2003, but is expected to be added later.


References
[1] Shanmugasundaram, et. al. (2000) Efficiently Publishing Relational Data as XML Documents, Proc. of the 26th International Conference on Very Large Databases, Cairo, Egypt, 65-76.

[2] IBM DB2 Universal Database, XML Extender Administration and Programming, Version 8

[3] IBM DB2 Universal Database, SQL Reference Volume 1, Version 8

[4] Oracle 9i, XML Developer’s Kit Guide – XDK, Release 2 (9.2)

[5] Oracle 9i, XML Database Developer’s Guide – Oracle XML DB, Release 2 (9.2)

[6] Microsoft SQL Server 2000 Documentation

[7] Microsoft SQL/XML 3.0 Documentation

[8] “XML-Related specs (SQL/XML)”. ISO/IEC JTC 1/SC 23 N00575,

WG3:HEL-026R2,H2-2000-331R2, Jim Melton, October 10, 2000

[9] Database Languages – SQL - Part 14: XML Related Specifications (SQL/XML), H2-2002-574, WG3:ZSH-020, Jim Melton (Editor), December 2002

  



ODBC driver | JDBC driver | ADO.NET provider | SQL/XML
XQuery | XML Editor | Legal | Privacy | Trademarks

Copyright © 1993-2004 DataDirect Technologies. All Rights Reserved.