6. XML Types

Oracle has some advanced XML Type support built into the database. XML data stored in the database is accessible wia JDBC using some Oracle specific classes.

The JDBC framework provided with the Spring Framework supports most of this already via SqlTypeValue and SqlReturnType. There is however a need for documentation and examples which are specifically targeted for an Oracle environment so teams can take advantage of this support and have a reference for best practices for the use of these features in an Oracle/Spring environment.

6.1 Dependencies

To use the Oracle XML support you need to use a couple of jar files available in the Oracle XML Developers Kit download available from Oracle. You need xdb.jar and also the xmlparserv2.jar since the XMLType depends on this parser library.

There is optional support for Spring's Object/XML Mapping support. If you use this support then you would also need the Spring Web Services project, version 1.0 or 1.5. The jar files needed from this project are spring-xml.jar, spring-oxm.jar and spring-oxm-tiger.jar.

All samples in this chapter access a table named "xml_table". Here is the DDL to create this table:

CREATE TABLE xml_table (
    id NUMBER(10),
    xml_text XMLTYPE,
    PRIMARY KEY (id));

6.2 Writing XML to an XMLTYPE column

To write XML data to a table you need to pass in the XML using a custom SqlTypeValue. In this implementation you would be responsible for setting the parameter value for the XML column in accordance with the API provided by the database driver.

For Oracle we provide a database specific implementation of an SqlXmlValue, which is an extension of the SqlTypeValue, that is easier to use. It works together with an SqlXmlHandler and adds an abstraction layer on top of the database specific APIs provided by the database vendors. There is a new SQLXML datatype in JDBC 4.0 that provides an abstraction, but so far it is not widely implemented.

In this example we have an XML value that we pass in as the second parameter. This XML value can be in the form of a String or an org.w3c.dom.Document. We use an SqlXmlHandler instance to gain access to a new instance of the SqlXmlValue. For the Oracle support the implementation classes are OracleXmlHandler and OracleXmlTypeValue respectively.

    simpleJdbcTemplate.update(
            "INSERT INTO xml_table (id, xml_text) VALUES (?, ?)",
            id,
            sqlXmlHandler.newSqlXmlValue(xml));1

1

We instantiate a new SqlXmlValue that will handle setting the parameter value for the XML.

The implementation of the SqlXmlHandler is chosen in the data access configuration file and should be injected into the DAO or Repository class.

<bean id="sqlXmlHandler" 
    class="org.springframework.data.jdbc.support.oracle.OracleXmlHandler"/>

Oracle's XMLType supports passing in an java.io.InputStream but since this is not supported by the JDBC 4.0 SQLXML datatype you will have to use the Oracle specific OracleXmlTypeValue directly.

    simpleJdbcTemplate.update(
            "INSERT INTO xml_table (id, xml_text) VALUES (?, ?)",
            id,
            new OracleXmlTypeValue(is));

6.3 Reading XML from an XMLTYPE column

Running a query against a table with an XMLTYPE column requires a RowMapper that can handle retrieval of the XMLType and the corrsponding XML data. The OracleXmlHandler provides several methods that supports easy access to the XML data. It is typically used in a RowMapper.

    String s = simpleJdbcTemplate.queryForObject(
            "SELECT xml_text FROM xml_table WHERE id = ?",
            new ParameterizedRowMapper<String>() {
                public String mapRow(ResultSet rs, int i) throws SQLException {
                    String s = sqlXmlHandler.getXmlAsString(rs, 1);1
                    return s;
                }
            },
            id);

1

We use the OracleXmlHandler to retreive the XML value as a String.

The XML data can be retreived as a String, a java.io.InputStream, a java.io.Reader or a javax.xml.transform.Source.

6.4 Marshalling an object to an XMLTYPE column

To map an object to XML and write this XML to a table you first need to use marshalling support available from the Spring Web Services project. Once the object data is marshalled to XML we can write the XML to a column in a database table. The latter part is very similar to the the XML support discussed above. We need to pass in the XML using a custom SqlTypeValue. In the object mapping implementation you would be responsible for marshalling the object to XML before setting the parameter value.

In this example we have an object that needs to be marshalled to XML. We are using a Marshaller provided by the Spring Web Services project. The marshaller is typically configured and then injected into the DAO or Repository. Here is an example configuration using the JAXB 2.0 support. In addition to JAXB 2.0, there is also support for JAXB 1.0, Casto, XML Beans, JiBX and XStream.

    <bean id="marshaller" class="org.springframework.oxm.jaxb.Jaxb2Marshaller">
        <property name="classesToBeBound">
            <list>
                <value>org.springframework.data.jdbc.samples.Item</value>
            </list>
        </property>
    </bean>

The JAXB 2.0 class that we are marshalling is a typical javaBean and it uses annotations for the meta data so there is no additional configuration needed.

package org.springframework.data.jdbc.samples;

import javax.xml.bind.annotation.*;
import java.math.BigDecimal;

@XmlRootElement(name = "item")
@XmlType(propOrder = {"name", "price"})
public class Item{
    private Long id = 0L;
    private String name;
    private BigDecimal price;

    @XmlAttribute(name="id")
    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    @XmlElement(name = "item-name")
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @XmlElement(name = "price")
    public BigDecimal getPrice() {
        return price;
    }

    public void setPrice(BigDecimal price) {
        this.price = price;
    }


    public String toString() {
        return "[" + id + "] " + name + " " + price;
    }
}

For Oracle we provide a database specific implementation of an SqlXmlMarshallingValue, which is an extension of the SqlXmlValue, that is easier to use. It works together with an SqlXmlObjectMappingHandler similar to the SqlXmlHandler that we used in the previous example. The object to be marshalled is passed in when the new instance of the SqlXmlValue is created.

For our dabase insert we pass in the marshalled value as the second parameter. The first parameter is the id of the object, and this will be use as the primary key for the row. We use an SqlXmlHandler instance to gain access to a new instance of the SqlXmlMappingValue. For the Oracle support the implementation classes are OracleXmlObjectMappingHandler and OracleXmlMarshallingValue respectively.

    simpleJdbcTemplate.update(
            "INSERT INTO xml_table (id, xml_text) VALUES (?, ?)",
            item.getId(),
            sqlXmlObjectMappingHandler
                    .newMarshallingSqlXmlValue(item));1

1

We instantiate a new marshalling SqlXmlValue that will handle mapping the parameter object to XML using a marshaller.

The implementation of the SqlXmlObjectMappingHandler is chosen in the data access configuration file and should be injected into the DAO or Repository class.

<bean id="sqlXmlHandler" 
    class="org.springframework.data.jdbc.support.oracle.OracleXmlObjectMappingHandler">
    <property name="marshaller" ref="marshaller"/>
</bean>

6.5 Unmarshalling an object from an XMLTYPE column

Last piece we need is reading the XML from the database and have it unmarshalled to an Item object. We will perform this work in a RowMapper together with the SqlXmlObjectMappingHandler.

    Item i = simpleJdbcTemplate.queryForObject(
            "SELECT xml_text FROM xml_table WHERE id = ?",
            new ParameterizedRowMapper<Item>() {
                public Item mapRow(ResultSet rs, int i) throws SQLException {
                    return (Item) sqlXmlObjectMappingHandler
                            .getXmlAsObject(rs, 1);1
                }
            },
            id);

1

We use the SqlXmlObjectMappingHandler to retreive the XML value and have it unmarshalled to an Item instance.

The XML data is unsmarshalled using an Unmarshaller which in the JAXB 2.0 case is also implemented by the Jaxb2Marshaller class. It must be injected into the unmarshaller property of the SqlXmlObjectMappingHandler. Since marshalling and unmarshalling is performed by the same object we pass in the bean named marshaller for the unmarshaller property.

<bean id="sqlXmlHandler" 
    class="org.springframework.data.jdbc.support.oracle.OracleXmlObjectMappingHandler">
    <property name="unmarshaller" ref="marshaller"/>
</bean>