Primero entendamos qué es el tipo XMLTYPE.
XMLTYPE es un tipo de datos único para Oracle desde 9i. Es una existencia poderosa que hereda la gota. Se puede usar para almacenar XML y proporciona muchas funciones de operación. En teoría, puede guardar datos 2G.
Entonces, ¿cómo se inserta los datos XMLType a través de Java? El proyecto usa mybatis, y siempre hay excepciones inexplicables. No puedo entender si es el problema de MyBatis o JDBC en sí, así que planeo hacerlo paso a paso, primero resolver JDBC y luego resolver mybatis.
JDBC
Después de un largo tiempo de lucha, descubrí que hay tres métodos principales para la operación JDBC:
1. Use xmltype como una cadena de cadena en Java, y la tarea específica de crear xmltype se entrega por completo a la base de datos:
String sql = "inserte en valores xmltables (xml) (sys.xmltype.createxml (?))"; String xmldata = "<Leltice> Este es un fragmento xml </selabel>"; Ps.Setstring (1, xmldata); Ps.ExecuteUpdate ();
Este método hará que el estrés de la base de datos sea demasiado, porque este método es simple y no requiere dependencias adicionales. Este método se usó al principio, pero durante el uso real, se descubrió que cuando la longitud del contenido excede aproximadamente 4000, lanzará: ORA-01461: puede unir un valor largo solo para insertar en una excepción de columna larga. Al principio pensé que la razón para usar mybatis seguía siendo la misma cuando usaba la prueba JDBC, y no había solución al usar muchos métodos. Es imposible guardar datos con una longitud de menos de 4000 cuando se usa este campo grande en el proyecto. De esta manera, usar VARCHAR2 es suficiente, por lo que se elimina este método.
2. Use el tipo de CLOB para operar. XMLTYPE hereda la existencia de CLOB, por lo que se puede operar a través de CLOB. El método es crear datos CLOB en el cliente y pasarlos a la base de datos para construir el valor XMLType a través de la función XMLType () de Oracle:
Cadena sql = "insertar en valores xmltables (xml) (xmltype (?))"; String xmldata = "<Leltice> Este es un fragmento xml </selabel>"; // Crear clobcLob tempcLob = clob.createTemporario (conexión, falso, clob.duration_session); // abrir CLOBTempCLOB.open (clob.mode_readwrite); // Get WriterWriter clobwriter = tempcLob.setcharacterstream (100); // Escribir datos clobwriter.write (xmldata); // frescos clobwriter.flush (); // cerrar escritorClobwriter.close (); // Cerrar clobtemplob.close (); pst.setObject (1, tempcLob);
Este cliente de método y base de datos son responsables de crear XMLType al mismo tiempo, por lo que la presión es relativamente promedio y no hay problema en exceder la longitud. Sin embargo, durante el uso real, se descubrió que el encabezado de contenido del XML no puede contener la siguiente información:
<? xml versión = "1.0" encoding = "utf-8"?>
De lo contrario, se lanzará una excepción:
Los nombres de PI que comienzan con XML están reservados
No hablemos de si encontrará algún problema de código confuso al procesar el contenido de la inclusión XML en chino en el futuro. Solo mirarlo hace que las personas se sientan incómodas, y los requisitos también requieren ahorro. No hay forma, y este método no funcionará.
3. Use la clase Oracle.xdb.xmltype proporcionada por Oracle. Después de que el cliente crea xmltype, el objeto se pasa directamente a la base de datos:
Connection conn = ...; // Obtener la conexión preparada PS = ...; // Get PrepareSatement String sql = "Insertar en valores XMLTABLE (XML) (?)"; String xmldata = "<Leltice> Este es un fragmento xml </selabel>"; // Crear un objeto xmltype xmltype xmltype = xmltype.createxml (conn, xmldata); Ps.SetObject (1, xmltype); Ps.ExecuteUpdate ();
Este método entrega completamente la tarea de crear XMLType al cliente, por lo que el cliente está bajo una gran presión y la base de datos está bajo baja presión. Durante la prueba real, se deben agregar dos paquetes JAR, de lo contrario, la clase no se puede encontrar error:
xdb.jarxmlparserv2.jar
Es necesario tener en cuenta que este paquete JAR no tiene anotación de versión, por lo que es fácil cometer errores. Al principio, descargué un xdb.jar, pero no importa cómo lo hice, se le solicitó que no pude encontrar una clase determinada. Después de verificar, descubrí que pertenece a una versión anterior de Oracle. Después de descargar un xdb.jar nuevamente, es normal.
Los tres métodos anteriores se compararon insertando 200,000 datos:
El primer método: el tiempo más corto y el consumo de CPU del servidor es el más grande;
El segundo método: se utiliza el tiempo más largo y el consumo de CPU del servidor está centrado;
El tercer método: lento y centrado, el consumo de CPU del servidor es mínimo.
En este punto, JDBC finalmente ha hecho algunas cosas pequeñas en la operación de datos de tipo XMLType. No hace falta decir que se adopta la tercera solución, pero el proyecto básicamente no usa directamente JDBC para operar. Por ejemplo, en el proyecto actual, se utiliza mybatis. Lo anterior también mencionó que siempre hay excepciones cuando se usa mybatis. Después de verificar mybatis, no hay implementación de XMLType. Parece que todavía hay algunos problemas, pero se ha hecho JDBC, por lo que la idea es clara, ¿verdad?
Mybatis
Usando mybatis para operar xmltype, también asignamos para encender el tipo en el lado de Java. Cuando la operación directa no realiza ningún procesamiento, como JDBC, todo es normal cuando el contenido transmitido es inferior a 4000. Cuando el contenido transmitido es más de 4000, también se lanza una excepción:
ORA-01461: puede unir un valor largo solo para insertar en una columna larga
Se puede ver que la operación de MyBatis es en realidad la misma que JDBC, excepto que encapsula una capa fuera de JDBC, para que podamos usar archivos de configuración y otros métodos de mapeo para acceder más convenientemente a la base de datos. Lo que debemos hacer es insertar datos de tipo XMLTYPE basados en la conveniencia original de MyBatis. En este caso, implementar un procesador TipoHandler personalizado de tipo XMLTYPE es la mejor opción.
Aquí, todavía usamos la solución tres mencionada anteriormente. Naturalmente, los dos paquetes JAR: xdb.jar y xmlparserv2.Jar también deben agregarse.
Agregue un XMLTyPeTypeHandler para implementar la interfaz TipoHandler. Dado que la inserción de datos utiliza principalmente el método SetParameter, solo este método se enumera aquí. Se omite el otro código de método:
/*** Oracle sys.xmltype Tipo de procesador personalizado*/public class xmltypeTypeHandler implementa typeHandler <String> {@Override public void setParameter (preparado PS, int i, parámetro de cadena, jdbctype jdbctype) lanza sqlexception {} ...}MyBatis utiliza este método SetParameter para establecer parámetros al insertar datos en la base de datos. En cuanto a los parámetros de este método, creo que ya ha entendido el código. Insertaremos el siguiente código aquí de acuerdo con el método de implementación de JDBC anterior:
Public void setParameter (preparado PS, int i, parámetro de cadena, jdbcType JdbcType) lanza SQLException {xmlType xmlType = xmltype.createxml (ps.getConnection (), parámetro); Ps.SetObject (i, xmltype);}E registre el convertidor en mapper-config.xml, porque en la enumeración definida por mybatis org.apache.ibatis.type.jdbctype, no hay un tipo xmltype que necesitemos, aquí lo definimos como indefinido:
<Configuration> <PypeHandlers> <typeHandler javatype = "String" jdbctype = "Undefined" handler = "com.tyyd.dw.context.xmltypetypeHandler"/> </pyinghandlers> </configuration>
En los parámetros del archivo de configuración, use nuestro convertidor definido para que MyBatis pueda encontrarlo:
#{xmlfile, jdbctype = undefined},Por supuesto, también puede estar más estandarizado y escribir su tipo y el convertidor que usa de manera completa:
#{xmlfile, javatype = string, jdbctype = undefined, typeHandler = com.tyyd.dw.context.xmltypeTypeHandler},
Complete los pasos anteriores y lógicamente todo está hecho, ejecutémoslo.
Se lanza el resultado: java.lang.ClassCastException: org.apache.commons.dbcp.PoolingDataSource$PoolGuardConnectionWrapper cannot be cast to oracle.jdbc.OracleConnection
No se puede convertir en el objeto de conexión de Oracle Oracleconnection. Después de verificar, descubrimos que nuestra fuente de datos usa DBCP de Apache, que debería ser incompatible con los dos. Revisé en línea y un tipo dijo que dio una solución perfecta, que es cargar una clase de controlador Oracle en el método SetParameter para crear una conexión, de la siguiente manera:
Class.forname ("oracle.jdbc.oracledriver"); conexión conexión = drivermanager.getConnection (url, nombre de usuario, contraseña);De hecho, esto puede resolver el problema de que el objeto de conexión no se puede convertir al 100%, pero en términos de implementación, jaja, todavía no comentaré. También hay personas que pasan por Internet, que dicen que pueden convertirse en un objeto PoolableConnection, y luego usan el método GetDelegate para obtener el enlace proxy original. Esto parece factible, intentemos:
PoolableConnection Connection = (PoolableConnection) Ps.getConnection (); xmltype xmltype = xmltype.createxml (conexión.getDelegate (), parámetro); ps.setObject (i, xmltype);
Como resultado, se lanzó otra excepción:
org.apache.commons.dbcp.PoolingDataSource$PoolGuardConnectionWrapper cannot be cast to org.apache.commons.dbcp.PoolableConnection , no se puede convertir.
No hay forma, parece que los artículos que circulan en línea no son confiables, por lo que no hay atajo, por lo que debe verificar el código fuente usted mismo.
Al observar el código fuente, descubrimos que PoolableConnection hereda la clase DelegatingConnection, y la clase DelegatingConnection implementa la interfaz de conexión. Vamos a convertirlo en una Conexión Delegating para probar:
DelegatingConnection Connection = (delegatingConnection) ps.getConnection (); xmltype xmltype = xmltype.createxml (conexión.getDelegate (), parámetro); ps.setObject (i, xmltype);
Como resultado, se lanzó una excepción: incapaz de construir descriptores: argumentos no válidos; La excepción anidada es Java.sql.sqlexception: No se puede construir descriptores: argumentos no válidos, a través de la depuración del punto de interrupción, descubrí que el objeto de conexión es realmente nulo. ¿Cómo podría ser nulo? La gente en Internet lo usa bien, pero no funcionará conmigo. Realmente es un dolor. Esto no es insoluble. ¿Realmente tienes que cargar una clase de conductor solo como dijo el tipo de arriba? No hay forma, estudiemos de nuevo.
Finalmente, descubrí que la conexión proxy original se puede obtener a través del método GetMetadata. Es tan brillante y la prueba es muy clara. Finalmente es normal y no es fácil. El código final es el siguiente:
@OverridePublic Void setParameter (preparado PS, int i, parámetro de cadena, jdbctype jdbctype) lanza SQLException {delegatingConnection Connection = (delegatingConnection) ps.getConnection (). GetMetadata () .getConnection (); Xmltype xmltype = xmltype.createxml (conexión.getDelegate (), parámetro); Ps.SetObject (i, xmltype);}En este punto, el uso de MyBatis para operar tipos de XMLType finalmente se ha realizado, y el proceso está lleno de giros y vueltas. Por supuesto, debe haber consultas cuando se insertan datos. A continuación, necesitamos implementar operaciones de consulta de tipo XMLTYPE.