There is some information in the Internet on how to incorporate XMLTYPE into Hibernate. However, there is a trick that needs to be done in order to make it working as expected. Because of that I decided to create this blog entry. In my example I will use Oracle 11.2.0.3 and Hibernate 4.
First of all you need to have:
- ojdbc6.jar (from Oracle's page),
- xdb6.jar (from Oracle's page),
- xmlparserv2.jar (I found this one in my SQL Developer distribution),
- javax.xml.parsers.DocumentBuilderFactory,
- javax.xml.parsers.SAXParserFactory,
- javax.xml.transform.TransformerFactory.
The second step is to provide UserType implementation (1):
import oracle.jdbc.OracleConnection;
import oracle.jdbc.OracleResultSet;
import oracle.xdb.XMLType;
import org.hibernate.HibernateException;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.usertype.UserType;
import org.w3c.dom.Document;
import java.io.Serializable;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Objects;
public class HibernateXMLType implements UserType
{
@Override
public int[] sqlTypes() {
return new int[] {
oracle.xdb.XMLType._SQL_TYPECODE
};
}
@Override
public Class returnedClass() {
return Document.class;
}
@Override
public int hashCode(Object o) {
return o.hashCode();
}
@Override
public Object assemble(Serializable cached, Object owner) {
try {
return new DocumentConverter().stringToDom((String) cached);
} catch (Exception e) {
throw new HibernateException("Could not assemble String to Document", e);
}
}
@Override
public Serializable disassemble(Object o) {
try {
return new DocumentConverter().domToString((Document) o);
} catch (Exception e){
throw new HibernateException("Could not disassemble Document to Serializable", e);
}
}
@Override
public Object replace(Object original, Object target, Object owner) {
return deepCopy(original);
}
@Override
public boolean equals(Object o1, Object o2) {
return Objects.equals(o1, o2);
}
@Override
public Object nullSafeGet(ResultSet rs, String[] names, SessionImplementor impl,
Object owner) throws SQLException {
XMLType xmlType = null;
try {
OracleResultSet ors = rs.unwrap(OracleResultSet.class);
xmlType = (XMLType) ors.getSQLXML(names[0]);
return xmlType != null ? xmlType.getDocument() : null;
} finally {
if (null != xmlType) {
xmlType.close();
}
}
}
@Override
public void nullSafeSet(PreparedStatement st, Object value, int index,
SessionImplementor session) throws SQLException {
XMLType xmlType = null;
try {
if (value != null) {
xmlType = XMLType.createXML(st.getConnection().unwrap(OracleConnection.class),
new DocumentConverter().domToString((Document) value));
}
st.setObject(index, xmlType);
} finally {
if (xmlType != null) {
xmlType.close();
}
}
}
@Override
public Object deepCopy(Object value) {
return value;
}
@Override
public boolean isMutable() {
return false;
}
}
DocumentConverter is my custom implementation and you can find similar methods here.Next, you need your custom Hibernate's dialect (2):
import oracle.xdb.XMLType;
import org.hibernate.dialect.Oracle10gDialect;
public class Oracle10gDialectWithXMLType extends Oracle10gDialect {
public Oracle10gDialectWithXMLType() {
registerHibernateType(XMLType._SQL_TYPECODE, "XMLTYPE");
registerColumnType(XMLType._SQL_TYPECODE, "XMLTYPE");
}
}
Finally you can place an annotation on your entity (3):
@Entity
@TypeDef(name = "HibernateXMLType", typeClass = HibernateXMLType.class).
After that you can define a field in your entity (4):
@Type(type = "HibernateXMLType")
private Document document;
This is all you need to utilize Oracle's XMLTYPE in Hibernate 4.
Nice Article..
ReplyDeleteMethod is ok :) we must create own dialect for all databases
ReplyDeleteCan you please provide the DocumentConverter implementation
ReplyDelete