Retrieving oracle XMLType stored as binary XML fro

2019-09-17 07:44发布

问题:

I outlined a solution to writing/reading to an Oracle XMLType column for Oracle 11g (specifically 11.2.0.1) if the XMLType column is stored as a CLOB, which is the default for 11.2.0.1.

REFERENCE ANSWER: Using oracle XMLType in a java spring hibernate scenario

In SQLDeveloper, if you view the SQL tab within a table definition (table DDL) it defines the XML columns storage in 11.2.0.1 like this:

 XMLTYPE COLUMN "ATTRIBUTE_XML2" STORE AS BASICFILE CLOB 

Problem: In Oracle 11.2.0.2, the default storage type for XMLType is Binary XML, which is preferred for performance reasons. It looks like this:

XMLTYPE COLUMN "ATTRIBUTE_XML2" STORE AS SECUREFILE BINARY XML

HOWEVER, once stored this way, my referenced solution no longer works for READING the XMLType column as XML from the database. It returns some garbage from xmlType.getStringVal(), which I assume is the oracle binary encoded XML.

Ultimate Problem I am unable to convert back from the oracle binxml format to a valid XML document.

I have tried the Oracle BinXMLProcessor, BinXMLStream, BinXMLDecoder and InfosetReader in various configurations as outlined by Oracle, but they seem to just read, rather than decode it - so it errors out.

Basic example within the nullSafeGet (see linked answer for context).

xmlType = (XMLType) rs.getObject(names[0]);
BinXMLProcessor xp = BinXMLProcessorFactory.createProcessor();
BinXMLStream bstr = xp.createBinXMLStream(xmlType.getInputStream());
BinXMLDecoder xdecode = bstr.getDecoder();
InfosetReader reader = xdecode.getReader();

while (reader.hasNext() && i < 25) {
    i++;
    reader.next();
    logger.debug("1 v:" + reader.getValue() + ", s:" + reader.getStandalone() + ", type: " + reader.getEventType() + ", " + reader.getDataLength());
}

I tried similar approaches, replacing xmlType.getInputStream() with xmlType.getBlobVal(178), xmlType.getBytesValue(), but they all throw an exception or return garbage data.

Huh.

回答1:

Found the catch, and it's not related to the code.

The proper nullSafeGet in the Hibernate UserType, as noted in the referenced answer is:

public void nullSafeSet(PreparedStatement st, Object value, int index, SessionImplementor session) throws HibernateException, SQLException {

    if (logger.isTraceEnabled()) {
        logger.trace("  nullSafeSet: " + value + ", ps: " + st + ", index: " + index);
    }
    try {
        XMLType xmlType = null;
        if (value != null) {
            xmlType = XMLType.createXML(getOracleConnection(st.getConnection()), (String)value);
        }
        st.setObject(index, xmlType);
    } catch (Exception e) {
        throw new SQLException("Could not convert String to XML for storage: " + (String)value);
    }
}

PROBLEM: when using a SECUREFILE BINARY XML column (not CLOB) you must use the most recent (11.2.0.2+) distribution of the xdb*.jar, which in this case is xdb6.jar (~257kb). The earlier xdb*.jar (~136kb for 10.x) will still function, without tossing any exceptions even when incorrectly decoding BINARY XML.

TL;DR: Download xdb6.jar (~257kb) from the Oracle 11gR2 (11.2.0.3) JDBC drivers page. Older xdb jars fail silently and will make you sad.