Mapping Oracle XmlType to Document

The following two classes allow you to use the Oracle XmlType object in a generic manner and support org.w3c.dom.Document objects that are greater than 4k and works with C3P0 and other database connection pools.

Most of the code in the OracleNativeExtractor is based upon Spring Frameworks C3P0NativeJdbcExtractor by Juergen Hoeller.

Good Luck!

-- sseaman

OracelXmlType.java

package myPackage;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.Serializable;
import java.io.StringWriter;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import oracle.xdb.XMLType;

import org.hibernate.HibernateException;
import org.hibernate.usertype.UserType;
import org.w3c.dom.Document;
import org.xml.sax.SAXException;

/*
 * This is a generic implementation of XmlType for use with Hibernate 3.0 or >
 * 
 * This will work with Document objects > 4k. (I tested with one that is 124k)
 * 
 * To use this class you need to have your Hibernate Object have the selected
 * object type as org.w3c.dom.Document in the java class and then have the
 * type in the hbm.xml defined as OracleXmlType
 * 
 * Example:
 * 
 * <code>
 * public class SomeClass {
 *         private Document xml;
 *         
 *         public Document getXml() { return xml; }
 *         public void setXml(Document _xml) { xml = _xml; }
 * }
 * 
 * SomeClass.hbm.xml: 
 * <property name="xml" column="XML" not-null="true" 
 *         type="mypackage.OracleXmlType"/>
 * 
 * </code> 
 * 
 * 
 * Oracle jars required:
 * xdb.jar
 * xmlparserv2.jar (Used by XmlType)
 * 
 *
 * @author sseaman
 */
public class OracleXmlType 
    implements UserType, Serializable 
{

    private static final long serialVersionUID = 2308230823023l;
    private static final Class returnedClass = Document.class;
    private static final int[] SQL_TYPES = { oracle.xdb.XMLType._SQL_TYPECODE };
   
    public int[] sqlTypes() {
        return SQL_TYPES;
    }

    public Class returnedClass() {
        return returnedClass;
    } 
    
    public int hashCode(Object _obj) {
        return _obj.hashCode();
    }
    
    public Object assemble(Serializable _cached, Object _owner) 
        throws HibernateException
    {
        try {
            return OracleXmlType.stringToDom((String)_cached);
        }
        catch (Exception e) {
            throw new HibernateException("Could not assemble String to Document", e);
        }        
    }
    
    public Serializable disassemble(Object _obj) 
        throws HibernateException
    {
        try {
            return OracleXmlType.domToString((Document)_obj);
        }
        catch (Exception e) {
            throw new HibernateException("Could not disassemble Document to Serializable", e);
        }
    }
    
    public Object replace(Object _orig, Object _tar, Object _owner) {
        return deepCopy(_orig);
    }
    
    public boolean equals(Object arg0, Object arg1) 
        throws HibernateException 
    {
        if(arg0 == null && arg1 == null) return true; 
        else if (arg0 == null && arg1 != null ) return false; 
        else return arg0.equals(arg1); 
    } 
    
    public Object nullSafeGet( ResultSet rs, String [ ] names, Object arg2 ) 
        throws HibernateException, SQLException 
    {
        XMLType xmlType = (XMLType)rs.getObject(names[0]);
        return (xmlType != null)?xmlType.getDOM():null;
    }

    public void nullSafeSet( PreparedStatement st, Object value, int index ) 
        throws HibernateException, SQLException 
    {
        OracleNativeExtractor extrator = new OracleNativeExtractor();
        Connection nativeConn = extrator.getNativeConnection(st.getConnection());
        
        try {
            XMLType xmlType = null;
            if (value != null) {
                xmlType = new oracle.xdb.XMLType( nativeConn, 
                    OracleXmlType.domToString((Document)value));
            }
            st.setObject(index, xmlType);
        }
        catch (Exception e) {
            throw new SQLException("Could not covert Document to String for storage");            
        }
    }
    
    public Object deepCopy(Object value) 
        throws HibernateException 
    {
        if (value == null) return null;
        
        return (Document)((Document)value).cloneNode(true);
    }
    
    public boolean isMutable() {
        return false;
    } 
    
    protected static String domToString(Document _document) 
        throws TransformerException
    {
        TransformerFactory tFactory = TransformerFactory.newInstance();
        Transformer transformer = tFactory.newTransformer();
        transformer.setOutputProperty(OutputKeys.INDENT, "yes");
        DOMSource source = new DOMSource(_document);
        StringWriter sw=new StringWriter();
        StreamResult result = new StreamResult(sw);
        transformer.transform(source, result);
        return sw.toString();
    }
    
    protected static Document stringToDom(String xmlSource) 
        throws SAXException, ParserConfigurationException, IOException 
    {
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        DocumentBuilder builder = factory.newDocumentBuilder();
        return builder.parse(new ByteArrayInputStream(xmlSource.getBytes("UTF-8")));
    }
    
}



OracleNativeExtractor.java

package myPackage;

import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.SQLException;

import oracle.jdbc.driver.OracleConnection;
import com.mchange.v2.c3p0.C3P0ProxyConnection;

 /**
  * Based upon Spring Frameworks C3P0NativeJdbcExtractor by Juergen Hoeller.
  * 
  * @author Juergen Hoeller (Changes by Sloan Seaman)
  * @since 1.1.5
  * @see com.mchange.v2.c3p0.C3P0ProxyConnection#rawConnectionOperation
  * @see SimpleNativeJdbcExtractor
  */
public class OracleNativeExtractor {

     public static Connection getRawConnection(Connection con) {
         return con;
     }
    
     /**
      * Retrieve the Connection via C3P0's <code>rawConnectionOperation</code> API,
      * using the <code>getRawConnection</code> as callback to get access to the
      * raw Connection (which is otherwise not directly supported by C3P0).
      * @see #getRawConnection
      */
     public Connection getNativeConnection(Connection con) throws SQLException {
         if (con instanceof OracleConnection) {
             return con;
         }
         else if (con instanceof C3P0ProxyConnection) {
             C3P0ProxyConnection cpCon = (C3P0ProxyConnection) con;
             try {
                 Method rawConnMethod = getClass().getMethod("getRawConnection", 
                    new Class[] {Connection.class});
                 return (Connection) cpCon.rawConnectionOperation(
                         rawConnMethod, null, new Object[] {C3P0ProxyConnection.RAW_CONNECTION});
             }
             catch (SQLException ex) {
                 throw ex;
             }
             catch (Exception ex) {
                 throw new SQLException("Error in reflection:"+ex.getMessage());
             }
         }
         else {
             Connection conTmp = con.getMetaData().getConnection();
             if (conTmp instanceof OracleConnection) return conTmp;
         }

         throw new SQLException("Could not find Native Connection of type OracleConnection");
     }

}