number example ejemplo java oracle jdbc blob clob

java - example - Oracle jdbc BLOB sobrecomplicado



number oracle to java (9)

Algunos descontentos encontrados para la segunda solución

Estoy usando ojdbc6.jar - la última versión y para la declaración de ''la segunda solución'':

BLOB blob = BLOB.createTemporary(oracleConnection, false, BLOB.DURATION_SESSION);

Tengo que liberar blob después de que se complete la declaración, o de lo contrario blob se cierra cuando se cierra la sesión (lo que puede llevar mucho tiempo con la agrupación de conexiones).

blob.freeTemporary();

De lo contrario, puede ver los recursos bloqueados:

select * from v$temporary_lobs

Otro problema con los BLOB temporales es la necesidad de asignar espacio de tabla temporal: según la documentación http://docs.oracle.com/cd/E11882_01/appdev.112/e18294.pdf

Gestión del espacio de tabla temporal para LOB temporales El tablespace temporal se utiliza para almacenar datos temporales de LOB

Cuando busco en la web la inserción de BLOB en la base de datos Oracle con el controlador jdbc thin, la mayoría de las páginas web sugieren un enfoque de 3 pasos:

  1. inserte el valor de empty_blob() .
  2. selecciona la fila con for update .
  3. inserte el valor real.

Esto funciona bien para mí, aquí hay un ejemplo:

Connection oracleConnection = ... byte[] testArray = ... PreparedStatement ps = oracleConnection.prepareStatement( "insert into test (id, blobfield) values(?, empty_blob())"); ps.setInt(1, 100); ps.executeUpdate(); ps.close(); ps = oracleConnection.prepareStatement( "select blobfield from test where id = ? for update"); ps.setInt(1, 100); OracleResultSet rs = (OracleResultSet) ps.executeQuery(); if (rs.next()) { BLOB blob = (BLOB) rs.getBLOB(1); OutputStream outputStream = blob.setBinaryStream(0L); InputStream inputStream = new ByteArrayInputStream(testArray); byte[] buffer = new byte[blob.getBufferSize()]; int byteread = 0; while ((byteread = inputStream.read(buffer)) != -1) { outputStream.write(buffer, 0, byteread); } outputStream.close(); inputStream.close(); }

Hay algunas páginas web donde los autores sugieren usar una solución más simple de 1 paso. Ejemplo anterior con esta solución:

Connection oracleConnection = ... byte[] testArray = ... PreparedStatement ps = oracleConnection.prepareStatement( "insert into test(id, blobfield) values(?, ?)"); BLOB blob = BLOB.createTemporary(oracleConnection, false, BLOB.DURATION_SESSION); OutputStream outputStream = blob.setBinaryStream(0L); InputStream inputStream = new ByteArrayInputStream(testArray); byte[] buffer = new byte[blob.getBufferSize()]; int byteread = 0; while ((byteread = inputStream.read(buffer)) != -1) { outputStream.write(buffer, 0, byteread); } outputStream.close(); inputStream.close(); ps.setInt(1, 100); ps.setBlob(2, blob); ps.executeUpdate(); ps.close();

El segundo código es mucho más fácil, entonces mi pregunta es: ¿Cuál es el punto de la primera solución (popular)? ¿Existe (hubo) algún tipo de restricción para la segunda solución (número de versión del servidor Oracle, versión del controlador jdbc, tamaño del blob, ...)? ¿Es la primera solución mejor (velocidad, consumo de memoria, ...)? ¿Alguna razón para no usar el segundo enfoque más simple?

La misma pregunta se aplica a los campos CLOB.


El enfoque de actualización que mencione en el primer caso se puede reescribir usando código JDBC puro y, por lo tanto, reducir su dependencia en las clases específicas de Oracle. Esto podría ser útil si su aplicación necesita ser independiente de la base de datos.

public static void updateBlobColumn(Connection con, String table, String blobColumn, byte[] inputBytes, String idColumn, Long id) throws SQLException { PreparedStatement pStmt = null; ResultSet rs = null; try { String sql = " SELECT " + blobColumn + " FROM " + table + " WHERE " + idColumn + " = ? " + " FOR UPDATE"; pStmt = con.prepareStatement(sql, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_UPDATABLE); pStmt.setLong(1, id); rs = pStmt.executeQuery(); if (rs.next()) { Blob blob = rs.getBlob(blobColumn); blob.truncate(0); blob.setBytes(1, inputBytes); rs.updateBlob(blobColumn, blob); rs.updateRow(); } } finally { if(rs != null) rs.close(); if(pStmt != null) pStmt.close(); } }

Para MSSQL entiendo que la sintaxis de bloqueo es diferente:

String sql = " SELECT " + blobColumn + " FROM " + table + " WITH (rowlock, updlock) " + " WHERE " + idColumn + " = ? "


El manejo de LOB del servidor Oracle es bastante pobre y puede sufrir graves problemas de rendimiento (p. Ej., Sobre uso masivo del registro de rehacer), por lo que la primera solución puede ser una forma de solucionarlos.

Sugeriría probar ambos enfoques. si tiene un DBA competente, es posible que pueda aconsejar qué enfoque tiene el menor impacto en el servidor.


Encontré una simple llamada a setObject(pos, byte[]) para mi caso. De la Programación de Bases de Datos con JDBC y Java Por George Reese,

byte[] data = null; stmt = con.prepareStatement("INSERT INTO BlobTest(fileName, " + "blobData) VALUES(?, ?)"); stmt.setString(1, "some-file.txt"); stmt.setObject(2, data, Types.BLOB); stmt.executeUpdate();


Esta declaración :

blob.setBytes(1, inputBytes);

está dando problemas cuando uso oracle client ojdbc14.jar, "Características no admitidas"

Entonces, tuve que trabajar por:

rset.next(); Blob bobj = rset.getBlob(1); BLOB object = (BLOB) bobj; int chunkSize = object.getChunkSize(); byte[] binaryBuffer = new byte[chunkSize]; int position = 1; int bytesRead = 0; int bytesWritten = 0, totbytesRead = 0, totbytesWritten = 0; InputStream is = fileItem.getInputStream(); while ((bytesRead = is.read(binaryBuffer)) != -1) { bytesWritten = object.putBytes(position, binaryBuffer, bytesRead); position += bytesRead; totbytesRead += bytesRead; totbytesWritten += bytesWritten; is.close();


Otro punto de vista de Oracle DBA. Los chicos de Sun hicieron muy mal trabajo cuando diseñaron los estándares JDBC (1.0, 2.0, 3.0, 4.0). BLOB significa objeto grande y, por lo tanto, puede ser muy grande. Es algo que no se puede almacenar en el montón de JVM. Oracle piensa que los BLOB son algo así como manejadores de archivos (de hecho, se llaman entonces "localizadores de lob"). Los LOBS no se pueden crear a través del constructor y no son objetos de Java. Además, los localizadores LOB (oracle.sql.BLOB) no se pueden crear a través del constructor, DEBEN crearse en el lado DB. En Oracle hay dos formas de cómo crear un LOB.

  1. DBMS_LOB.CREATETEMPORATY: el localizador devuelto en este caso apunta al espacio de tabla temporal. Todas las escrituras / lecturas contra este localizador se enviarán a través de la red al servidor de BD. No se almacena nada en el montón de JVM.

  2. Llamar a la función EMPTY_BLOB. INSERTAR EN T1 (NOMBRE, ARCHIVO) VALORES ("a.avi", EMPTY_BLOB ()) ¿ENVIAR ARCHIVO A? En este caso, los puntos del localizador de lob vuelven al espacio de tabla de datos. Todas las escrituras / lecturas contra este localizador se enviarán a través de la red al servidor de BD. Todas las escrituras son "guardadas" por escrituras en redo-logs. No se almacena nada en el montón de JVM. La cláusula de devolución no era compatible con los estándares JDBC (1.0, 2.0), por lo tanto, puede encontrar muchos ejemplos en Internet donde las personas recomiendan el enfoque de dos pasos: "INSERTAR ...; SELECCIONAR ... PARA ACTUALIZAR";

Los lobús de Oracle se deben asociar con alguna conexión de base de datos, no se pueden usar cuando la conexión de DB se pierde / se cierra / (o "se compromete"). No se pueden pasar de una conexión a otra.

El segundo ejemplo puede funcionar, pero requerirá una copia excesiva si los datos del espacio de tabla temporal en el espacio de tabla de datos.


Si el tamaño de insertar BLOB es mayor que blob.getBufferSize () , la transacción se confirma tan pronto como se escribe el primer fragmento en db, ya que el valor predeterminado de la propiedad autoCommit de la conexión jdbc es verdadero y las escrituras posteriores fallan ya que db las trata como nuevas transacciones. Se sugiere lo siguiente:
a) Establezca la propiedad jdbc connection autoCommit en false.

conn.setAutoCommit(false);

b) Comprometer explícitamente la transacción después de cargar todo el BLOB.

while ((bytesRead = messageInputStream.read(buffer)) != -1) { cumBytes += bytesRead; blobOutputStream.write(buffer, 0, bytesRead); } conn.commit();


Siempre que los datos CLOB sean lo suficientemente pequeños como para que quepan en su memoria sin explotar, puede simplemente crear una declaración preparada y simplemente llamar

ps.setString(1, yourString);

Puede haber otras limitaciones de tamaño, pero parece funcionar para los tamaños que estamos tratando (500kB max).


Una cosa interesante con JDBC es que puede actualizar agresivamente a los últimos controladores y trabajar con las características de JDBC 4.0. Los controladores Oracle JDBC funcionarán con versiones de bases de datos más antiguas, por lo que puede usar un controlador JDBC de marca 11g en una base de datos 10g. La base de datos Oracle 11g JDBC viene en dos formas: ojdbc5.jar para Java 5 (es decir, JDK 1.5) y ojdbc6.jar para Java 6 (es decir, JDK 1.6). El archivo ojdbc6.jar admite la nueva especificación JDBC 4.0.

Con los controladores más recientes / jdbc 4.0 puede crear Blobs y Clobs fuera del objeto de conexión:

Blob aBlob = con.createBlob(); int numWritten = aBlob.setBytes(1, val);