Oracle
 sql >> Teknologi Basis Data >  >> RDS >> Oracle

Menggunakan kolom Oracle XMLType di hibernate

Arah dan Persyaratan Saya

  • Entitas harus menyimpan XML sebagai string (java.lang.String)
  • Database harus mempertahankan XML dalam kolom XDB.XMLType
    • Memungkinkan pengindeksan dan kueri jenis xpath/ExtractValue/xquery yang lebih efisien
  • Konsolidasikan selusin solusi parsial yang saya temukan selama seminggu terakhir
  • Lingkungan Kerja
    • Oracle 11g r2 x64
    • Hibernasi 4.1.x
    • Java 1.7.x x64
    • Windows 7 Pro x64

Solusi Langkah demi Langkah

Langkah 1:Temukan xmlparserv2.jar (~1350kb)

Jar ini diperlukan untuk mengkompilasi langkah 2, dan disertakan dalam instalasi Oracle di sini:%ORACLE_11G_HOME%/LIB/xmlparserv2.jar

Langkah 1.5:Temukan xdb6.jar (~257kb)

Ini penting jika Anda menggunakan Oracle 11gR2 11.2.0.2 atau lebih tinggi, atau menyimpan sebagai BINARY XML.

Kenapa?

  • Dalam 11.2.0.2+ kolom XMLType disimpan menggunakan SECUREFILE BINARYXML secara default, sedangkan versi sebelumnya akan disimpan sebagai BASICFILECLOB
  • Versi lama xdb*.jar tidak memecahkan kode xml biner dengan benar dan gagal secara diam-diam
    • Google Oracle Database 11g Rilis 2 Driver JDBC dan unduh xdb6.jar
  • Diagnosis dan solusi untuk masalah decoding XML Biner diuraikan di sini

Langkah 2:Buat UserType hibernasi untuk Kolom XMLType

Dengan Oracle 11g dan Hibernate 4.x, ini lebih mudah daripada kedengarannya.

public class HibernateXMLType implements UserType, Serializable {
static Logger logger = Logger.getLogger(HibernateXMLType.class);


private static final long serialVersionUID = 2308230823023l;
private static final Class returnedClass = String.class;
private static final int[] SQL_TYPES = new int[] { oracle.xdb.XMLType._SQL_TYPECODE };

@Override
public int[] sqlTypes() {
    return SQL_TYPES;
}

@Override
public Class returnedClass() {
    return returnedClass;
}

@Override
public boolean equals(Object x, Object y) throws HibernateException {
    if (x == null && y == null) return true;
    else if (x == null && y != null ) return false;
    else return x.equals(y);
}


@Override
public int hashCode(Object x) throws HibernateException {
    return x.hashCode();
}

@Override
public Object nullSafeGet(ResultSet rs, String[] names, SessionImplementor session, Object owner) throws HibernateException, SQLException {

    XMLType xmlType = null;
    Document doc = null;
    String returnValue = null;
    try {
        //logger.debug("rs type: " + rs.getClass().getName() + ", value: " + rs.getObject(names[0]));
        xmlType = (XMLType) rs.getObject(names[0]);

        if (xmlType != null) {
            returnValue = xmlType.getStringVal();
        }
    } finally {
        if (null != xmlType) {
            xmlType.close();
        }
    }
    return returnValue;
}

@Override
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);
    }
}


@Override
public Object deepCopy(Object value) throws HibernateException {
    if (value == null) {
        return null;
    } else {
        return value;
    }
}

@Override
public boolean isMutable() {
    return false;
}

@Override
public Serializable disassemble(Object value) throws HibernateException {
    try {
        return (Serializable)value;
    } catch (Exception e) {
        throw new HibernateException("Could not disassemble Document to Serializable", e);
    }
}

@Override
public Object assemble(Serializable cached, Object owner) throws HibernateException {

    try {
        return (String)cached;
    } catch (Exception e) {
        throw new HibernateException("Could not assemble String to Document", e);
    }
}

@Override
public Object replace(Object original, Object target, Object owner) throws HibernateException {
    return original;
}



private OracleConnection getOracleConnection(Connection conn) throws SQLException {
    CLOB tempClob = null;
    CallableStatement stmt = null;
    try {
        stmt = conn.prepareCall("{ call DBMS_LOB.CREATETEMPORARY(?, TRUE)}");
        stmt.registerOutParameter(1, java.sql.Types.CLOB);
        stmt.execute();
        tempClob = (CLOB)stmt.getObject(1);
        return tempClob.getConnection();
    } finally {
        if ( stmt != null ) {
            try {
                stmt.close();
            } catch (Throwable e) {}
        }
    }
}   

Langkah 3:Beri anotasi pada bidang di entitas Anda.

Saya menggunakan anotasi dengan pegas/hibernasi, bukan memetakan file, tetapi saya membayangkan sintaksnya akan serupa.

@Type(type="your.custom.usertype.HibernateXMLType")
@Column(name="attribute_xml", columnDefinition="XDB.XMLTYPE")
private String attributeXml;

Langkah 4:Menangani kesalahan appserver/junit sebagai akibat dari Oracle JAR

Setelah menyertakan %ORACLE_11G_HOME%/LIB/xmlparserv2.jar (1350kb) di classpath Anda untuk mengatasi kesalahan kompilasi, sekarang Anda mendapatkan kesalahan waktu proses dari server aplikasi Anda...

http://www.springframework.org/schema/beans/spring-beans-3.1.xsd<Line 43, Column 57>: XML-24509: (Error) Duplicated definition for: 'identifiedType'
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd<Line 61, Column 28>: XML-24509: (Error) Duplicated definition for: 'beans'
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd<Line 168, Column 34>: XML-24509: (Error) Duplicated definition for: 'description'
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd<Line 180, Column 29>: XML-24509: (Error) Duplicated definition for: 'import'
... more ...

MENGAPA KESALAHAN?

xmlparserv2.jar menggunakan JAR Services API (Mekanisme Penyedia Layanan) untuk mengubah kelas javax.xml default yang digunakan untuk SAXParserFactory, DocumentBuilderFactory, dan TransformerFactory.

BAGAIMANA TERJADINYA?

Javax.xml.parsers.FactoryFinder mencari implementasi kustom dengan memeriksa, dalam urutan ini, variabel lingkungan, %JAVA_HOME%/lib/jaxp.properties, kemudian untuk file konfigurasi di bawah META-INF/services di classpath, sebelum menggunakan implementasi default disertakan dengan JDK (com.sun.org.*).

Di dalam xmlparserv2.jar terdapat direktori META-INF/services, yang diambil oleh kelas javax.xml.parsers.FactoryFinder. File-file tersebut adalah sebagai berikut:

META-INF/services/javax.xml.parsers.DocumentBuilderFactory (which defines oracle.xml.jaxp.JXDocumentBuilderFactory as the default)
META-INF/services/javax.xml.parsers.SAXParserFactory (which defines oracle.xml.jaxp.JXSAXParserFactory as the default)
META-INF/services/javax.xml.transform.TransformerFactory (which defines oracle.xml.jaxp.JXSAXTransformerFactory as the default)

SOLUSI?

Ganti ketiganya kembali, jika tidak, Anda akan melihat kesalahan aneh.

  • javax.xml.parsers.* memperbaiki kesalahan yang terlihat
  • javax.xml.transform.*memperbaiki kesalahan penguraian XML yang lebih halus
    • dalam kasus saya, dengan konfigurasi Apache commons membaca/menulis

SOLUSI CEPAT untuk mengatasi kesalahan startup server aplikasi:Argumen JVM

Untuk mengganti perubahan yang dibuat oleh xmlparserv2.jar, tambahkan properti JVM berikut ke argumen startup server aplikasi Anda. Logika java.xml.parsers.FactoryFinder akan memeriksa variabel lingkungan terlebih dahulu.

-Djavax.xml.parsers.SAXParserFactory=com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl -Djavax.xml.parsers.DocumentBuilderFactory=com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl -Djavax.xml.transform.TransformerFactory=com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl

Namun, jika Anda menjalankan uji kasus menggunakan @RunWith(SpringJUnit4ClassRunner.class) atau yang serupa, Anda masih akan mengalami kesalahan.

SOLUSI LEBIH BAIK untuk kesalahan startup server aplikasi DAN kesalahan kasus uji? 2 opsi

Opsi 1:Gunakan argumen JVM untuk server aplikasi dan pernyataan @BeforeClass untuk kasus pengujian Anda

System.setProperty("javax.xml.parsers.DocumentBuilderFactory","com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl");
System.setProperty("javax.xml.parsers.SAXParserFactory","com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl");
System.setProperty("javax.xml.transform.TransformerFactory","com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl");

Jika Anda memiliki banyak kasus uji, ini menjadi menyakitkan. Bahkan jika Anda memasukkannya ke dalam super.

Opsi 2:Buat file definisi Penyedia Layanan Anda sendiri di classpath kompilasi/runtime untuk proyek Anda, yang akan menimpa file yang disertakan dalam xmlparserv2.jar

Dalam proyek pegas maven, timpa pengaturan xmlparserv2.jar dengan membuat file berikut di direktori %PROJECT_HOME%/src/main/resources:

%PROJECT_HOME%/src/main/resources/META-INF/services/javax.xml.parsers.DocumentBuilderFactory (which defines com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl as the default)
%PROJECT_HOME%/src/main/resources/META-INF/services/javax.xml.parsers.SAXParserFactory (which defines com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl as the default)
%PROJECT_HOME%/src/main/resources/META-INF/services/javax.xml.transform.TransformerFactory (which defines com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl as the default)

File-file ini direferensikan oleh server aplikasi (tidak diperlukan argumen JVM), dan menyelesaikan masalah pengujian unit apa pun tanpa memerlukan perubahan kode apa pun.

Selesai.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Mengulangi baris berdasarkan nilai kolom di setiap baris

  2. Mengekspor Tabel Oracle ke Lembar Kerja Excel

  3. Cara Memeriksa Nilai Parameter NLS di Oracle Database

  4. Nama kolom tabel Oracle dengan spasi

  5. Apa cara terbaik untuk menghubungkan antara database Android dan Oracle?