Estoy tratando de escribir y actualizar un documento en PDF en una columna de blob, pero solo puedo actualizar el blob solo escribiendo más datos que los datos almacenados previamente. Si trato de actualizar la columna de blob con datos de documentos más pequeños, obtengo solo un pdf dañado.Cómo escribir/actualizar Oracle blob de manera confiable?
Primero, la columna de blob se ha inicializado utilizando la función empty_blob(). Escribí la clase Java de ejemplo a continuación para probar este comportamiento. Lo ejecuto la primera vez con 'verdadero' como primer parámetro del método principal, así que en la primera fila se almacena un documento de aproximadamente 31kB y en la segunda fila hay un documento de 278kB. Luego lo ejecuto con 'falso' como parámetro, de esta manera las dos filas deberían actualizarse intercambiando los documentos. El resultado es que obtengo un resultado correcto solo cuando escribo más datos que el existente.
¿Cómo es posible escribir un método que escribe y actualiza un blob de manera confiable sin preocuparse por el tamaño de los datos binarios?
import static org.apache.commons.io.IOUtils.copy;
import java.io.FileInputStream;
import java.io.OutputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import oracle.jdbc.OracleDriver;
import oracle.jdbc.OracleResultSet;
import oracle.sql.BLOB;
import org.apache.commons.lang.ArrayUtils;
/**
* Prerequisites:
* 1) a table named 'x' must exists [create table x (i number, j blob);]
* 2) that table should have two columns [insert into x (i, j) values (1, empty_blob()); insert into x (i, j) values (2, empty_blob()); commit;]
* 3) download lsp.pdf from http://www.objectmentor.com/resources/articles/lsp.pdf
* 4) download dotguide.pdf from http://www.graphviz.org/Documentation/dotguide.pdf
*/
public class UpdateBlob {
public static void main(String[] args) throws Exception {
processFiles(new String[]{"lsp.pdf", "dotguide.pdf"}, Boolean.valueOf(args[0]));
}
public static void processFiles(String [] fileNames, boolean forward) throws Exception {
if(!forward){
ArrayUtils.reverse(a);
}
int idx = 1;
for(String fname : fileNames){
insert(idx++, fname);
}
}
private static void insert(int idx, String fname) throws Exception{
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
DriverManager.registerDriver(new OracleDriver());
conn = DriverManager.getConnection("jdbc:oracle:thin:@"+db+":"+port+":"+sid, user, pwd);
ps = conn.prepareStatement("select j from x where i = ? for update");
ps.setLong(1, idx);
rs = ps.executeQuery();
if (rs.next()) {
FileInputStream instream = new FileInputStream(fname);
BLOB blob = ((OracleResultSet)rs).getBLOB(1);
OutputStream outstream = blob.setBinaryStream(1L);
copy(instream, outstream);
instream.close();
outstream.close();
}
rs.close();
ps.close();
conn.close();
} catch (SQLException e) {
e.printStackTrace();
throw new Exception(e);
}
}
}
versión de Oracle: 11.1.0.7.0 - 64 bits
Incluso probé la API JDBC estándar sin necesidad de utilizar uno específico de Oracle (como en el ejemplo anterior) sin ningún éxito.
Quizás me falta algo obvio ...Pero, ¿dónde se define el método 'copy'? Mi corazonada es que el método 'copy' no está haciendo lo que esperabas y solo está reemplazando los primeros N bytes si los nuevos datos son más pequeños que los datos anteriores. –
Por cierto: no debe abrir una nueva conexión para cada llamada de la función. Eso va a ser muy lento. –
@JustinCave 'método de copia 'se define en [org.apache.commons.io.IOUtils] (http://commons.apache.org/io/api-release/org/apache/commons/io/IOUtils.html #copy (java.io.InputStream,% 20java.io.OutputStream)) clase. Hay una importación estática al comienzo del código. Las importaciones estáticas son menos legibles, pero creo que en algunos casos es conveniente usarlas. – alexyz78