7 Using the XQuery Processor for Java

This chapter explains how to use the Oracle XML Developer's Kit (XDK) XQuery processor for Java.

Topics:

7.1 Introduction to the XQuery Processor for Java

XDK provides a standalone XQuery processor for use by Java applications. XQuery is the World Wide Web Consortium (W3C) standard query language for Extensible Markup Language (XML). Using XQuery to process XML within a Java application can improve developer productivity and application performance. Applications written with XQuery often require less code, run faster, and use less memory than applications written fully in Java.

JSR 225: XQuery API for Java (XQJ) defines how queries can be executed from a Java application. To use XQJ, your application must run with Java version 1.6. In addition, these JAR files are required:

  • jlib/oxquery.jar

  • jlib/xqjapi.jar

  • jlib/orai18n-mapping.jar

  • lib/xmlparserv2.jar

  • xdk/jlib/apache-xmlbeans.jar

The directory paths for these Java Archive (JAR) files are relative to the ORACLE_HOME directory of your Oracle Database installation.

Example 7-1 shows how to execute a simple "Hello World" query using XQuery API for Java (XQJ). Because the XQuery processor runs directly in the Java Virtual Machine (JVM), you need no database or server to run this example. The example prints the output <hello-world>2</hello-world>.

This chapter describes the features and extensions that are specific to the Oracle implementation of XQuery. General information about XQuery and XQJ is documented outside of this document.

See Also:

Note:

Oracle also implements XQuery and XQJ as part of Oracle XML DB. See Using XQuery API for Java to Access Oracle XML DB for details about Oracle XML DB.

Example 7-1 Simple Query Using XQJ

import javax.xml.xquery.XQConnection;
import javax.xml.xquery.XQException;
import javax.xml.xquery.XQPreparedExpression;
import javax.xml.xquery.XQSequence;
 
import oracle.xml.xquery.OXQDataSource;
 
public class HelloWorld {
    
    public static void main(String[] args) throws XQException {
        OXQDataSource ds = new OXQDataSource();
        XQConnection con = ds.getConnection();
        String query = "<hello-world>{1 + 1}</hello-world>";
        XQPreparedExpression expr = con.prepareExpression(query);
        XQSequence result = expr.executeQuery();

        // prints "<hello-world>2</hello-world>"
        System.out.println(result.getSequenceAsString(null));
        
        result.close();
        expr.close();
        con.close();
    }
    
}

7.2 Entity Resolution

XDK extends XQJ with an entity resolver framework for controlling how documents, schemas, modules, collations, and external functions are obtained during query processing. The examples in this section show how to use an entity resolver for several types of entities. See the class oracle.xml.xquery.OXQEntity in Oracle Database XML Java API Reference for a complete list of the types of entities that the query processor can request.

This section includes these subsections:

7.2.1 Document Resolution

The example in this section shows how you can use an entity resolver to determine which document is returned by the fn:doc function.

Example 7-2 displays the contents of books.xml.

Example 7-3 displays the contents of books.xq.

Example 7-4 shows how to execute the query books.xq with a custom entity resolver.

The example generates this output:

<title>A Game of Thrones</title>

The instance of MyEntityResolver is passed to the XQuery processor by setting it on the connection. The XQuery processor invokes the entity resolver during query processing to get the document to be returned by the fn:doc function.

Example 7-2 books.xml

<books>
  <book>
    <title>A Game of Thrones</title>
    <author><first>George</first><last>Martin</last></author>
    <price>10.99</price>
  </book>
  <book>
    <title>The Pillars of the Earth</title>
    <author><first>Ken</first><last>Follett</last></author>
    <price>7.99</price>
  </book>
</books>

Example 7-3 books.xq

for $book in fn:doc('books.xml')/books/book 
where xs:decimal($book/price) gt 10.00
return
  $book/title

Example 7-4 Executing a Query with a Custom Entity Resolver

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.URI;
 
import javax.xml.xquery.XQConnection;
import javax.xml.xquery.XQException;
import javax.xml.xquery.XQPreparedExpression;
import javax.xml.xquery.XQSequence;
import javax.xml.xquery.XQStaticContext;
 
import oracle.xml.xquery.OXQConnection;
import oracle.xml.xquery.OXQDataSource;
import oracle.xml.xquery.OXQEntity;
import oracle.xml.xquery.OXQEntityKind;
import oracle.xml.xquery.OXQEntityLocator;
import oracle.xml.xquery.OXQEntityResolver;
import oracle.xml.xquery.OXQEntityResolverRequestOptions;
import oracle.xml.xquery.OXQView;

public class ResolveDocument {
 
    private static class MyEntityResolver extends OXQEntityResolver {
        @Override
        public OXQEntity resolveEntity(OXQEntityKind kind, OXQEntityLocator locator,
                OXQEntityResolverRequestOptions options) throws IOException {
            if (kind == OXQEntityKind.DOCUMENT) {
                URI systemId = locator.getSystemIdAsURI();
                if ("file".equals(systemId.getScheme())) {
                    File file = new File(systemId);
                    FileInputStream input = new FileInputStream(file);
                    OXQEntity result = new OXQEntity(input);
                    result.enlistCloseable(input);
                    return result;
                }
            }
            return null;
        }
    }
 
    public static void main(String[] args) throws XQException, IOException {
        OXQDataSource ds = new OXQDataSource();
        XQConnection con = ds.getConnection();
 
        // OXQView is used to access Oracle extensions on XQJ objects.
        OXQConnection ocon = OXQView.getConnection(con);
        ocon.setEntityResolver(new MyEntityResolver());
 
        File query = new File("books.xq");
        
        // Relative URIs are resolved against the base URI before invoking the entity resolver.
        // The relative URI 'books.xml' used in the query will be resolved against this URI.
        XQStaticContext ctx = con.getStaticContext();
        ctx.setBaseURI(query.toURI().toString());
 
        FileInputStream queryInput = new FileInputStream(query);
        XQPreparedExpression expr = con.prepareExpression(queryInput, ctx);
        queryInput.close();
        XQSequence result = expr.executeQuery();
        System.out.println(result.getSequenceAsString(null));
        
        result.close();
        expr.close();
        con.close();
    }
}

7.2.2 External Function Resolution

For each external function that is declared in a query, the entity resolver is called with the entity kind oracle.xml.xquery.OXQEntityKind.EXTERNAL_FUNCTION. The oracle.xml.xquery.OXQEntityLocator instance that is passed in the call to the entity resolver provides the name of the XQuery function and its argument types. The entity resolver can then return any class that extends oracle.xml.xquery.OXQFunctionEvaluator and has a public constructor. Then, the XQuery processor instantiates the returned class. When the XQuery external function call is evaluated, the evaluate() method is invoked.

Example 7-6 shows how you can use an entity resolver to define the implementation of an XQuery external function.

Example 7-5 displays the contents of trim.xq.

Example 7-6 runs trim.xq, and shows how to define the implementation of an external function.

The example generates this output:

<result>John Doe</result>

The external function util:trim is used to remove white space from the beginning and end of a string value. This function is implemented in Java and called within the query.

In Example 7-6, the entity resolver returned a class that extends OXQFunctionEvaluator. In some cases, it might be more convenient to return a Java static method instead of a class. When a static method is returned, the query processor automatically maps the method arguments and the return value to the XQuery data model, as defined by the XQJ specification.

Example 7-7 runs trim.xq again, but this time the external function is bound to a Java static method.

Again, the example generates this output:

<result>John Doe</result>

Example 7-5 trim.xq

declare namespace util = "http://example.com/util";
 
declare function util:trim($arg as xs:string) as xs:string external;
 
(: a string with surrounding white space :)
declare variable $input := "   John Doe    ";
 
<result>{util:trim($input)}</result>

Example 7-6 Defining the Implementation of an External Function

import java.io.FileInputStream;
import java.io.IOException;
import java.util.Collections;
 
import javax.xml.namespace.QName;
import javax.xml.xquery.XQConnection;
import javax.xml.xquery.XQException;
import javax.xml.xquery.XQPreparedExpression;
import javax.xml.xquery.XQSequence;
 
import oracle.xml.xquery.OXQConnection;
import oracle.xml.xquery.OXQDataSource;
import oracle.xml.xquery.OXQEntity;
import oracle.xml.xquery.OXQEntityKind;
import oracle.xml.xquery.OXQEntityLocator;
import oracle.xml.xquery.OXQEntityResolver;
import oracle.xml.xquery.OXQEntityResolverRequestOptions;
import oracle.xml.xquery.OXQFunctionContext;
import oracle.xml.xquery.OXQFunctionEvaluator;
import oracle.xml.xquery.OXQFunctionMetaData;
import oracle.xml.xquery.OXQView;

public class ResolveExternalFunction {
 
    public static class TrimFunction extends OXQFunctionEvaluator {
        @Override
        public XQSequence evaluate(OXQFunctionContext context, XQSequence[] params) throws XQException {
            XQConnection con = context.getConnection();
            XQSequence arg = params[0];
            String value = arg.getSequenceAsString(null);
            String trimmed = value.trim();
            return con.createSequence(Collections.singleton(trimmed).iterator());
        }
    }
    
    private static class MyEntityResolver extends OXQEntityResolver {
        @Override
        public OXQEntity resolveEntity(OXQEntityKind kind, OXQEntityLocator locator,
                OXQEntityResolverRequestOptions options) throws XQException, IOException {
            if (kind == OXQEntityKind.EXTERNAL_FUNCTION) {
                OXQFunctionMetaData metaData = (OXQFunctionMetaData)locator.getExtension();
                QName name = metaData.getName();
                int arity = metaData.getParameterTypes().length;
                if ("http://example.com/util".equals(name.getNamespaceURI()) &&
                    "trim".equals(name.getLocalPart()) && arity == 1) {
                    return new OXQEntity(TrimFunction.class);
                }
            }
            return null;
        }
    }

    public static void main(String[] args) throws IOException, XQException {
        OXQDataSource ds = new OXQDataSource();
        XQConnection con = ds.getConnection();
        OXQConnection ocon = OXQView.getConnection(con);
        ocon.setEntityResolver(new MyEntityResolver());
 
        FileInputStream query = new FileInputStream("trim.xq");
        XQPreparedExpression expr = con.prepareExpression(query);
        query.close();

        XQSequence result = expr.executeQuery();
        System.out.println(result.getSequenceAsString(null));
 
        result.close();
        expr.close();
        con.close();
    }
}

Example 7-7 Binding an External Function to a Java Static Method

import java.io.FileInputStream;
import java.io.IOException;
import java.lang.reflect.Method;
 
import javax.xml.namespace.QName;
import javax.xml.xquery.XQConnection;
import javax.xml.xquery.XQException;
import javax.xml.xquery.XQPreparedExpression;
import javax.xml.xquery.XQSequence;
 
import oracle.xml.xquery.OXQConnection;
import oracle.xml.xquery.OXQDataSource;
import oracle.xml.xquery.OXQEntity;
import oracle.xml.xquery.OXQEntityKind;
import oracle.xml.xquery.OXQEntityLocator;
import oracle.xml.xquery.OXQEntityResolver;
import oracle.xml.xquery.OXQEntityResolverRequestOptions;
import oracle.xml.xquery.OXQFunctionMetaData;
import oracle.xml.xquery.OXQView;

public class ResolveExternalFunction2 {
 
    public static String trim(String value) {
        return value.trim();
    }
    
    private static class MyEntityResolver extends OXQEntityResolver {
        @Override
        public OXQEntity resolveEntity(OXQEntityKind kind, OXQEntityLocator locator,
                OXQEntityResolverRequestOptions options) throws XQException, IOException {
            if (kind == OXQEntityKind.EXTERNAL_FUNCTION) {
                OXQFunctionMetaData metaData = (OXQFunctionMetaData)locator.getExtension();
                QName name = metaData.getName();
                int arity = metaData.getParameterTypes().length;
                if ("http://example.com/util".equals(name.getNamespaceURI()) &&
                    "trim".equals(name.getLocalPart()) && arity == 1) {
                    Method staticMethod = null;
                    try {
                        staticMethod = ResolveExternalFunction2.class.getMethod("trim", String.class); 
                    } catch (NoSuchMethodException e) {
                        throw new IllegalStateException(e);
                    }
                    return new OXQEntity(staticMethod);
                }
            }
            return null;
        }
    }

    public static void main(String[] args) throws IOException, XQException {
        OXQDataSource ds = new OXQDataSource();
        XQConnection con = ds.getConnection();
        OXQConnection ocon = OXQView.getConnection(con);
        ocon.setEntityResolver(new MyEntityResolver());
 
        FileInputStream query = new FileInputStream("trim.xq");
        XQPreparedExpression expr = con.prepareExpression(query);
        query.close();
 
        XQSequence result = expr.executeQuery();
        System.out.println(result.getSequenceAsString(null));
 
        result.close();
        expr.close();
        con.close();
    }
}

7.2.3 Module Resolution

An XQuery library module provides functions and variables that can be imported by other modules. For each imported module, the entity resolver is called with the entity kind oracle.xml.xquery.OXQEntityKind.MODULE. Using the oracle.xml.xquery.OXQEntityLocator instance, you can invoke the getSystemId() method to get the location of the module being imported. If no location is provided in the module import, you can invoke the method getNamespace() to get the target namespace of the module. The entity resolver can then return the corresponding library module.

The example in this section shows how you can use an entity resolver to control the resolution of XQuery library modules.

Example 7-8 displays the contents of math.xq.

Example 7-9 displays the contents of main.xq.

Example 7-10 shows how to execute a query that imports a library module.

The example generates this output:

20.546015931

The query main.xq imports the library module math.xq, and then invokes the function math:circumference to compute the circumference of a circle.

Example 7-8 math.xq

module namespace math = "http://example.com/math";
 
declare variable $math:pi as xs:decimal := 3.14159265;
 
declare function math:circumference($diameter as xs:decimal) {
   $math:pi * $diameter
};

Example 7-9 main.xq

import module namespace math = "http://example.com/math" at "math.xq";
 
math:circumference(6.54)

Example 7-10 Executing a Query that Imports a Library Module

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.URI;
 
import javax.xml.xquery.XQConnection;
import javax.xml.xquery.XQException;
import javax.xml.xquery.XQPreparedExpression;
import javax.xml.xquery.XQSequence;
import javax.xml.xquery.XQStaticContext;
 
import oracle.xml.xquery.OXQConnection;
import oracle.xml.xquery.OXQDataSource;
import oracle.xml.xquery.OXQEntity;
import oracle.xml.xquery.OXQEntityKind;
import oracle.xml.xquery.OXQEntityLocator;
import oracle.xml.xquery.OXQEntityResolver;
import oracle.xml.xquery.OXQEntityResolverRequestOptions;
import oracle.xml.xquery.OXQView;

public class ResolveLibraryModule {
 
    private static class MyEntityResolver extends OXQEntityResolver {
        @Override
        public OXQEntity resolveEntity(OXQEntityKind kind, OXQEntityLocator locator,
                OXQEntityResolverRequestOptions options) throws IOException {
            if (kind == OXQEntityKind.MODULE) {
                URI systemId = locator.getSystemIdAsURI();
                if (systemId != null && "file".equals(systemId.getScheme())) {
                    File file = new File(systemId);
                    FileInputStream input = new FileInputStream(file);
                    OXQEntity result = new OXQEntity(input);
                    result.enlistCloseable(input);
                    return result;
                }
            }
            return null;
        }
    }

    public static void main(String[] args) throws XQException, IOException {
        OXQDataSource ds = new OXQDataSource();
        XQConnection con = ds.getConnection();
 
        // OXQView is used to access Oracle extensions on XQJ objects.
        OXQConnection ocon = OXQView.getConnection(con);
        ocon.setEntityResolver(new MyEntityResolver());
 
        File query = new File("main.xq");
        
        // Relative URIs are resolved against the base URI before invoking the entity resolver.
        // The relative URI 'math.xq' used in the query will be resolved against this URI.
        XQStaticContext ctx = con.getStaticContext();
        ctx.setBaseURI(query.toURI().toString());
        
        FileInputStream queryInput = new FileInputStream(query);
        XQPreparedExpression expr = con.prepareExpression(queryInput, ctx);
        queryInput.close();

        XQSequence result = expr.executeQuery();
        System.out.println(result.getSequenceAsString(null));
        
        result.close();
        expr.close();
        con.close();
    }
}

7.2.4 Schema Resolution

An XQuery schema import imports element declarations, attributes declarations, and type definitions from an XML schema. You can use imported declarations and definitions in a query to validate and test data instances.

The example in this section shows how you can use an entity resolver to control which XML schema is used when a query imports a schema.

Example 7-11 displays the contents of size.xsd.

Example 7-12 displays the contents of size.xq.

Example 7-13 shows how to execute a query that imports a schema.

The example generates this output:

S INVALID:big XL INVALID:42

The query size.xq uses the type shirt-size defined in schema size.xsd to test a list of values.

Example 7-11 size.xsd

<xs:schema 
  xmlns:xs="http://www.w3.org/2001/XMLSchema"
  targetNamespace="http://example.com/size">
 
  <xs:simpleType name="shirt-size">
    <xs:restriction base="xs:string">
      <xs:enumeration value="XS"/>
      <xs:enumeration value="S"/>
      <xs:enumeration value="M"/>
      <xs:enumeration value="L"/>
      <xs:enumeration value="XL"/>
    </xs:restriction>
  </xs:simpleType>
 
</xs:schema>

Example 7-12 size.xq

import schema namespace ns = "http://example.com/size" at "size.xsd";
 
for $size in ("S", "big", "XL", 42)
return
  if ($size castable as ns:shirt-size) then
    ns:shirt-size($size)
  else
    concat("INVALID:", $size)

Example 7-13 Executing a Query that Imports a Schema

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.URI;
 
import javax.xml.xquery.XQConnection;
import javax.xml.xquery.XQException;
import javax.xml.xquery.XQPreparedExpression;
import javax.xml.xquery.XQSequence;
import javax.xml.xquery.XQStaticContext;
 
import oracle.xml.xquery.OXQConnection;
import oracle.xml.xquery.OXQDataSource;
import oracle.xml.xquery.OXQEntity;
import oracle.xml.xquery.OXQEntityKind;
import oracle.xml.xquery.OXQEntityLocator;
import oracle.xml.xquery.OXQEntityResolver;
import oracle.xml.xquery.OXQEntityResolverRequestOptions;
import oracle.xml.xquery.OXQView;

public class ResolveSchema {
 
    private static class MyEntityResolver extends OXQEntityResolver {
        @Override
        public OXQEntity resolveEntity(OXQEntityKind kind, OXQEntityLocator locator,
                OXQEntityResolverRequestOptions options) throws IOException {
            if (kind == OXQEntityKind.SCHEMA) {
                URI systemId = locator.getSystemIdAsURI();
                if (systemId != null && "file".equals(systemId.getScheme())) {
                    File file = new File(systemId);
                    FileInputStream input = new FileInputStream(file);
                    OXQEntity result = new OXQEntity(input);
                    result.enlistCloseable(input);
                    return result;
                }
            }
            return null;
        }
    }
  
   public static void main(String[] args) throws XQException, IOException {
        OXQDataSource ds = new OXQDataSource();
        XQConnection con = ds.getConnection();
 
        // OXQView is used to access Oracle extensions on XQJ objects.
        OXQConnection ocon = OXQView.getConnection(con);
        ocon.setEntityResolver(new MyEntityResolver());
 
        File query = new File("size.xq");
        
        // Relative URIs are resolved against the base URI before invoking the entity resolver.
        // The relative URI 'math.xq' used in the query will be resolved against this URI.
        XQStaticContext ctx = con.getStaticContext();
        ctx.setBaseURI(query.toURI().toString());
        
        FileInputStream queryInput = new FileInputStream(query);
        XQPreparedExpression expr = con.prepareExpression(queryInput, ctx);
        queryInput.close();
        
        XQSequence result = expr.executeQuery();
        System.out.println(result.getSequenceAsString(null));
        
        result.close();
        expr.close();
        con.close();
    }
}

7.2.5 Prefabricated Entity Resolvers

XDK includes several implementations of OXQEntityResolver that you can use for common tasks such as file system and HTTP resolution. In the previous examples that resolve entities using the file system, you could replace MyEntityResolver with the file entity resolver that is available in XDK.

The example in this section shows how you can run the query in Example 7-3 without having to implement your own entity resolver.

Example 7-14 shows how to execute the query books.xq with a prefabricated file resolver.

Example 7-14 generates this output:

<title>A Game of Thrones</title>

An instance of the factory oracle.xml.xquery.OXQFileResolverFactory is created from the connection. Then, this factory is used to create an entity resolver that resolves schemas, modules, and documents against the file system. By contrast with this example, Example 7-4 uses the custom entity resolver MyEntityResolver to resolve only documents against the file system.

XDK provides these entity resolver factories:

  • oracle.xml.xquery.OXQFileResolverFactory: Creates an entity resolver that resolves 'file:' URIs for schema, module, and document locations.

  • oracle.xml.xquery.OXQHttpResolverFactory: Creates an entity resolver that resolves 'http:' URIs for schema, module, and document locations.

  • oracle.xml.xquery.OXQCompositeResolverFactory: Creates an entity resolver that delegates requests to other entity resolvers. For any kind of request, the resolver returns the first nonnull result it receives from one of the delegate resolvers.

  • oracle.xml.xquery.OXQJavaResolverFactory: Creates an entity resolver that resolves external functions and modules to Java static methods or classes.

See Also:

the package oracle.xml.xquery in Oracle Database XML Java API Reference for API information about these factory interfaces

Example 7-14 Executing a Query with a Prefabricated File Resolver

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
 
import javax.xml.xquery.XQConnection;
import javax.xml.xquery.XQException;
import javax.xml.xquery.XQPreparedExpression;
import javax.xml.xquery.XQSequence;
import javax.xml.xquery.XQStaticContext;
 
import oracle.xml.xquery.OXQConnection;
import oracle.xml.xquery.OXQDataSource;
import oracle.xml.xquery.OXQFileResolverFactory;
import oracle.xml.xquery.OXQView;

public class ResolverFactory {
    public static void main(String[] args) throws XQException, IOException {
        OXQDataSource ds = new OXQDataSource();
        XQConnection con = ds.getConnection();
 
        // OXQView is used to access Oracle extensions on XQJ objects.
        OXQConnection ocon = OXQView.getConnection(con);
        OXQFileResolverFactory factory = ocon.createEntityResolverFactory(OXQFileResolverFactory.class);
        ocon.setEntityResolver(factory.createResolver());
 
        File query = new File("books.xq");
        
        // Relative URIs are resolved against the base URI before invoking the entity resolver.
        // The relative URI 'books.xml' used in the query will be resolved against this URI.
        XQStaticContext ctx = con.getStaticContext();
        ctx.setBaseURI(query.toURI().toString());
 
        FileInputStream queryInput = new FileInputStream(query);
        XQPreparedExpression expr = con.prepareExpression(queryInput, ctx);
        queryInput.close();
        
        XQSequence result = expr.executeQuery();
        System.out.println(result.getSequenceAsString(null));
        
        result.close();
        expr.close();
        con.close();
    }
}

7.3 Performance and Scalability

The XDK XQuery processor provides several features for improving the performance and scalability of your application.

This section includes these subsections:

7.3.1 Streaming Query Evaluation

The XDK XQuery processor for Java supports streaming evaluation for many types of queries. Streaming evaluation requires a small amount of main memory, even when the input XML is very large.

To facilitate streaming evaluation, these actions are recommended:

  • Set the binding mode on the static context to deferred mode (see the method javax.xml.xquery.XQStaticContext.setBindingMode(int) in Oracle Database XML Java API Reference). If the binding mode is not deferred, the input XML is fully materialized when it is bound.

  • Provide the input XML as an instance of java.io.InputStream, java.io.Reader, or javax.xml.stream.XMLStreamReader. Input XML is provided to the query processor by binding it to the expression, or by returning it from an entity resolver.

  • Ensure that the javax.xml.xquery.XQSequence instance is consumed in a way that does not require materialization:

    • The string serialization methods getSequenceAsString(...) and getItemAsString(...) produce data as a string that is held in memory. Instead, use the writeSequence(...) or the writeItem(...) method to serialize the sequence.

    • The getNode() method builds a Document Object Model (DOM) node that is held in memory. Instead, consider using the getSequenceAsStream() or the getItemAsStream() method to get a Streaming API for XML (StAX) stream.

    • The getItem() method copies and materializes the current item in memory. Instead, use methods directly on the java.xml.xquery.XQSequence instance to access the current item (see the interface javax.xml.xquery.XQItemAccessor in Oracle Database XML Java API Reference).

The example described in this section invokes a query using XQJ in a way that does not prevent streaming evaluation.

Example 7-15 displays the contents of books2.xq.

Example 7-16 sets up the query to enable streaming evaluation.

Example 7-16 writes this output to the file results.xml:

<title>A Game of Thrones</title>

The binding mode is set to the value BINDING_MODE_DEFERRED to avoid materializing books.xml when it is bound to the prepared expression. Likewise, the result is written to an output stream, and it is not materialized.

To simplify the example, the input file books.xml is small. Even if this file contained millions of books, evaluating the query would require only a small maximum heap size because only one book element is held in memory at one time. In contrast with the query books.xq, shown in Example 7-3, the query books2.xq does not require you to define an entity resolver. Both examples (books.xq and books2.xq) are streamable.

Example 7-15 books2.xq

declare variable $doc external;
 
for $book in $doc/books/book
where xs:decimal($book/price) gt 10.00
return
  $book/title

Example 7-16 Facilitating Streaming Evaluation

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
 
import javax.xml.namespace.QName;
import javax.xml.xquery.XQConnection;
import javax.xml.xquery.XQConstants;
import javax.xml.xquery.XQException;
import javax.xml.xquery.XQPreparedExpression;
import javax.xml.xquery.XQSequence;
import javax.xml.xquery.XQStaticContext;
 
import oracle.xml.xquery.OXQDataSource;
 
public class Streaming {
 
    public static void main(String[] args) throws XQException, IOException {
        OXQDataSource ds = new OXQDataSource();
        XQConnection con = ds.getConnection();
        
        XQStaticContext ctx = con.getStaticContext();
        ctx.setBindingMode(XQConstants.BINDING_MODE_DEFERRED);
        con.setStaticContext(ctx);
        
        FileInputStream input = new FileInputStream("books.xml");
        FileInputStream query = new FileInputStream("books2.xq");
        FileOutputStream output = new FileOutputStream("result.xml");
        
        XQPreparedExpression expr = con.prepareExpression(query);
        query.close();
        expr.bindDocument(new QName("doc"), input, null, null);
        
        XQSequence result = expr.executeQuery();
        result.writeSequence(output, null);
        
        result.close();
        input.close();
        output.close();
        expr.close();
        con.close();
    }
}

7.3.2 External Storage

Depending on the query, the processor might have to store part of the input XML in main memory during query evaluation. For example, this scenario can occur in cases such as these:

  • A sequence is sorted.

  • The value bound to a variable is used multiple times.

  • A path expression uses a reverse-axis step.

To reduce memory usage in such cases, you can configure the XQuery processor to use external storage for materializing XML, rather than main memory. To enable the use of external storage, set the data source property OXQConstants.USE_EXTERNAL_STORAGE to true, and set an oracle.xml.scalable.PageManager instance on the dynamic context.

Note:

Using external storage can significantly reduce the amount of main memory that is consumed during query processing. However, it can also reduce performance.

Example 7-17 shows how to enable the XQuery processor to use disk-based storage rather than main memory when XML is materialized.

Example 7-17 writes this output to the file results.xml:

<title>A Game of Thrones</title>

Example 7-17 Configuring the XQuery Processor to Use External Storage

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
 
import javax.xml.namespace.QName;
import javax.xml.xquery.XQConnection;
import javax.xml.xquery.XQConstants;
import javax.xml.xquery.XQException;
import javax.xml.xquery.XQPreparedExpression;
import javax.xml.xquery.XQSequence;
import javax.xml.xquery.XQStaticContext;
 
import oracle.xml.scalable.FilePageManager;
import oracle.xml.xquery.OXQDataSource;
import oracle.xml.xquery.OXQPreparedExpression;
import oracle.xml.xquery.OXQView;

public class ExternalStorage {
 
    public static void main(String[] args) throws XQException, IOException {
        OXQDataSource ds = new OXQDataSource();
        ds.setProperty(OXQDataSource.USE_EXTERNAL_STORAGE, "true");
        
        XQConnection con = ds.getConnection();
        XQStaticContext ctx = con.getStaticContext();
        ctx.setBindingMode(XQConstants.BINDING_MODE_DEFERRED);
        con.setStaticContext(ctx);
        
        FileInputStream input = new FileInputStream("books.xml");
        FileInputStream query = new FileInputStream("books2.xq");
        FileOutputStream output = new FileOutputStream("results.xml");
        
        XQPreparedExpression expr = con.prepareExpression(query);
        query.close();
        expr.bindDocument(new QName("doc"), input, null, null);
        
        // Set a page manager that will be used by the XQuery processor if XML needs to be materialized
        OXQPreparedExpression oexpr = OXQView.getPreparedExpression(expr);
        File temporaryFile = File.createTempFile("books", ".pagefile");
        temporaryFile.deleteOnExit();
        oexpr.setPageManager(new FilePageManager(temporaryFile.getAbsolutePath()));
        
        XQSequence result = expr.executeQuery();
        result.writeSequence(output, null);
        
        result.close();
        input.close();
        output.close();
        expr.close();
        con.close();
    }
}

7.3.3 Thread Safety

The Oracle implementation of XQJ is not threadsafe. For example, an instance of javax.xml.xquery.XQSequence must be accessed by only one thread. However, a restricted form of thread safety is supported for managing instances of javax.xml.xquery.XQConnection.

  • An instance of XQConnection serves as a factory for creating instances of XQExpression, XQPreparedExpression, XQItem, XQSequence, XQItemType, and XQSequenceType. One thread can manage the creation of these objects for use by other threads. For example, XQPreparedExpression instances created in one thread by the same connection can be used in other threads. Each XQPreparedExpression instance, however, must be executed by only one thread. Any user-defined implementations of oracle.xml.xquery.OXQEntityResolver that are specified must be threadsafe when expressions from the same connection are evaluated concurrently.

  • The XQConnection.close() method closes all XQExpression and XQPreparedExpression instances that were geted from the connection. Closing those instances closes all XQResultSequence and XQResultItem instances obtained from the expressions. The XQConnection.close() method can be called while expressions obtained from the connection are being processed in other threads. In this case, all registered resources held by the expressions (such as java.io.InputStream and java.io.Reader) are closed. This contract assumes that all registered resources support a threadsafe close method. For example, many JDK implementations of java.io.Closeable satisfy this requirement. But, many implementations of javax.xml.stream.XMLStreamReader do not provide a threadsafe close method. Implementations without this support can give unpredictable results if they are closed while a second thread is still reading (see the interface oracle.xml.xquery.OXQCloseable in Oracle Database XML Java API Reference).

    See Also:

    the method oracle.xml.xquery.OXQConnection.copyExpression(XQPreparedExpression) in Oracle Database XML Java API Reference

7.4 Performing Updates

XDK extends XQJ with the ability to execute updating queries. XML documents can be read as an instance of javax.xml.xquery.XQItem, and then modified using XQuery Update Facility extensions. This feature is disabled by default. You can enable it by setting the update mode on the dynamic context to oracle.xml.xquery.OXQConstants.UPDATE_MODE_ENABLED.

Documents to be updated must be bound in deferred mode (see the method javax.xml.xquery.XQStaticContext.setBindingMode(int) in Oracle Database XML Java API Reference). If the binding mode is not set to deferred, the input bindings are copied before query execution. Thus, only the copy is updated.

The example in this section shows how you can modify an XML document using the XQuery Update Facility.

Example 7-18 displays the contents of configuration.xml.

Example 7-19 displays the contents of update.xq.

Example 7-20 displays the contents of configuration.xml after an update.

Example 7-21 shows how execute the query update.xq.

In the example, these actions occur:

  1. The XML file configuration.xml is read as an instance of javax.xml.xquery.XQItem.

  2. The item is bound to the prepared expression for the query update.xq.

  3. The query update.xq is executed.

  4. The modified document is written to the file configuration.xml.

See Also:

Example 7-18 configuration.xml

<configuration>
  <property>
    <name>hostname</name>
    <value>example.com</value>
  </property>
  <property>
    <name>timeout</name>
    <value>1000</value>
  </property>
</configuration>

Example 7-19 update.xq

declare variable $doc external;
 
let $timeout := $doc/configuration/property[name eq "timeout"]
return
  replace value of node $timeout/value 
  with 2 * xs:integer($timeout/value)

Example 7-20 Updated File configuration.xml

<configuration>
  <property>
    <name>hostname</name>
    <value>example.com</value>
  </property>
  <property>
    <name>timeout</name>
    <value>2000</value>
  </property>
</configuration>

Example 7-21 Executing the Updating Query update.xq

import java.io.FileInputStream;
import java.io.IOException;
import java.io.FileOutputStream;
 
import javax.xml.namespace.QName;
import javax.xml.xquery.XQConnection;
import javax.xml.xquery.XQConstants;
import javax.xml.xquery.XQException;
import javax.xml.xquery.XQItem;
import javax.xml.xquery.XQPreparedExpression;
import javax.xml.xquery.XQStaticContext;
 
import oracle.xml.xquery.OXQConstants;
import oracle.xml.xquery.OXQDataSource;
import oracle.xml.xquery.OXQView;
 
public class UpdateDocument {
    public static void main(String[] args) throws XQException, IOException {
        OXQDataSource ds = new OXQDataSource();
        XQConnection con = ds.getConnection();
        
        XQStaticContext ctx = con.getStaticContext();
        // Set the binding mode to deferred so the document
        // item is not copied when it is bound.
        ctx.setBindingMode(XQConstants.BINDING_MODE_DEFERRED);
        con.setStaticContext(ctx);
 
        FileInputStream input = new FileInputStream("configuration.xml");
        XQItem doc = con.createItemFromDocument(input, null, null);
        input.close();
        
        System.out.println("Before update: \n" + doc.getItemAsString(null));
        
        FileInputStream query = new FileInputStream("update.xq");
        XQPreparedExpression expr = con.prepareExpression(query);
        query.close();
        expr.bindItem(new QName("doc"), doc);
        // Enable updates (disabled by default)
        OXQView.getDynamicContext(expr).setUpdateMode(OXQConstants.UPDATE_MODE_ENABLED);
        expr.executeQuery();
 
        System.out.println("After update: \n" + doc.getItemAsString(null));
        
        // Write the modified document back to the file
        FileOutputStream out = new FileOutputStream("configuration.xml");
        doc.writeItem(out, null);

        expr.close();
        con.close();
    }
}

7.5 Standards and Specifications — XQuery Processor for Java

The XDK XQuery processor for Java conforms with these standards and specifications:

Note:

The XDK XQuery processor for Java is not interoperable with other XQJ implementations, including the Oracle XQJ implementation for Oracle XML DB. (See JSR-225: XQuery API for Java for the meaning of interoperable.)

This section includes these subsections:

7.5.1 Optional Features

The XQuery specification defines certain features as optional. Table 7-1 lists the optional XQuery features supported by XDK.

7.5.2 Implementation-Defined Items

The XQJ and XQuery specifications leave the definition of certain aspects up to the implementation. The tables in this section briefly describe the implementation-defined items for XDK.

Table 7-2 summarizes the XQJ implementation-defined items.

Table 7-2 XQJ Implementation-Defined Items

Description Behavior

Class name of XQDataSource implementation

oracle.xml.xquery.OXQDataSource

Properties defined on OXQDataSource

None. The username and password are silently ignored.

JDBC connection support

JDBC connections are not supported.

Commands

Not supported.

Cancelling of query execution with method XQPreparedExpression.cancel()

Yes

Serialization

Yes

Additional StAX or SAX events

None

User-defined schema types

Yes

Node identity, document order, and full-node context preservation when a node is bound to an external variable

Not preserved.

Login timeout

Not supported.

Transactions

Not supported. An exception is thrown if a transaction method is called.

XQItemAccessor.getNodeUri() method behavior if the input node is not a document node

Exception

XQItemType.getTypeName() method for anonymous types

A unique name.

XQItemType.getSchemaURI() method

The schema URI is returned when a type is created from XQJ. No otherwise.

XQDataFactory.createItemFromDocument() and bindDocument() methods if the input is not a well-formed XML document

Exception

Additional error codes returned by class XQQueryException

The qualified names of Oracle-specific error codes are in the namespace http://xmlns.oracle.com/xdk/xquery/errors

ConnectionPoolXQDataSource, PooledXQConnection, XQConnectionEvent, XQConnectionEventListener interfaces

No

XQDataSource.getConnection(java.sql.Connection)

JDBC connections are not supported. An exception is thrown if this method is called.

XQDataSource.getConnection(java.lang.String, java.lang.String)

Same as getConnection(). Parameters are ignored.

Note:

XDK support for the features in Table 7-2 differs from the Oracle XML DB support for them.

Table 7-3 summarizes the XQuery implementation-defined items.

Table 7-3 XQuery Implementation-Defined Items

Item Behavior

The version of Unicode that is used to construct expressions

4.0

The statically-known collations

Unicode codepoint collation and collations derived from classes java.text.Collator or oracle.i18n.text.OraCollator

The implicit time zone.

Uses the default time zone, as determined by method java.util.Calendar.getInstance()

The circumstances in which warnings are raised, and the ways in which warnings are handled

None

The method by which errors are reported to the external processing environment

Exception javax.xml.xquery.XQException

Whether the implementation is based on the rules of XML 1.0 and XML Names, or the rules of XML 1.1 and XML Names 1.1

1.0

Any components of the static context or dynamic context that are overwritten or augmented by the implementation

See Table 7-5

Which of the optional axes are supported by the implementation, if the Full-Axis Feature is not supported

Full support

The default handling of empty sequences returned by an ordering key (sortspec) in an order by clause (empty least or empty greatest)

least

The names and semantics of any extension expressions (pragmas) recognized by the implementation

None

The names and semantics of any option declarations recognized by the implementation

None

Protocols (if any) by which parameters can be passed to an external function, and the result of the function can be returned to the invoking query

Defined by XQJ

The process by which the specific modules to be imported by a module import are identified, if the Module feature is supported (includes processing of location hints, if any)

Entity resolvers

Any static typing extensions supported by the implementation, if the Static Typing feature is supported

Strict mode (based on subtype) and optimistic mode (based on type intersection). Optimistic mode is the default.

The means by which serialization is invoked, if the Serialization feature is supported

Defined by XQJ

The default values for the byte-order-mark, encoding, media-type, normalization-form, omit-xml-declaration, standalone, and version parameters, if the Serialization feature is supported

See the interface oracle.xml.xquery.OXQSerializationParameters in Oracle Database XML Java API Reference.

Limits on ranges of values for various data types, as enumerated in XQuery 1.0 Specification, Section 5.3

Decimal and integer values have arbitrary precision.

Table 7-4 summarizes the XQuery Update Facility implementation-defined items.

Table 7-4 XQuery Update Facility Implementation-Defined Items

Item Behavior

The revalidation modes that are supported by this implementation.

skip

The default revalidation mode for this implementation.

skip

The mechanism (if any) by which an external function can return an XDM instance, or a pending update list, or both to the invoking query.

Returning a pending update list from an external function is not supported.

The semantics of fn:put(), including the kinds of nodes accepted as operands by this function.

Any node type is accepted. Storage of the node is determined by the entity resolver. See the class oracle.xml.xquery.OXQEntity in Oracle Database XML Java API Reference, specifically the documentation for the entity kind UPD_PUT.

Table 7-5 summarizes the default initial values for the static context.

Table 7-5 Default Initial Values for the Static Context

Context Component Default Value

Statically known namespaces

fn=http://www.w3.org/2005/xpath-functions

xml=http://www.w3.org/XML/1998/namespace

xs=http://www.w3.org/2001/XMLSchema

xsi=http://www.w3.org/2001/XMLSchema-instance

local=http://www.w3.org/2005/xquery-local-functions

ora-ext=http://xmlns.oracle.com/xdk/xquery/extension

ora-java=http://xmlns.oracle.com/xdk/xquery/java

ora-xml=http://xmlns.oracle.com/xdk/xquery/xml

ora-fn=http://xmlns.oracle.com/xdk/xquery/function

Prefixes that begin with ora- are reserved for use by Oracle. Additional prefixes that begin with ora- may be added to the default statically known namespaces in a future release.

Default element/type namespace

No namespace

Default function namespace

fn

In-scope schema types

Built-in types in xs

In-scope element declarations

None

In-scope attribute declarations

None

In-scope variables

None

Context item static type

item()

Function signatures

Functions in the fn namespace, and constructors for built-in atomic types

Statically known collations

Unicode codepoint collation and collations derived from classes java.text.Collator or oracle.i18n.text.OraCollator

Default collation

Unicode codepoint collation:

http://www.w3.org/2005/xpath-functions/collation/codepoint

Construction mode

preserve

Ordering mode

ordered

Default order for empty sequences

least

Boundary-space policy

strip

Copy-namespaces mode

preserve, no-inherit

Base URI

As defined in standard

Statically known documents

None

Statically known collections

None

Statically known default collection type

node()*