Я пытаюсь написать и обновить документ pdf в столбце больших двоичных объектов, но я просто могу обновить большой двоичный объект, только записывая больше данных, чем ранее сохраненные данные . Если я попытаюсь обновить столбец BLOB-объектов меньшими данными документа, я получу только поврежденный PDF-файл.
Сначала столбец blob был инициализирован с помощью функции empty_blob (). Я написал образец класса Java ниже, чтобы проверить это поведение. Я запускаю его в первый раз с «true» в качестве первого параметра метода main , поэтому в первой строке хранится документ размером около 31 КБ, а во второй строке - документ размером 278 КБ. Затем я запускаю его с параметром false, таким образом, две строки должны быть обновлены, меняя местами документы. В результате я получаю правильный результат только тогда, когда записываю больше данных, чем существующее.
Как можно написать метод, который записывает и обновляет большой двоичный объект надежным способом, не беспокоясь о размере двоичных данных?
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);
}
}
}
Версия Oracle: 11.1.0.7.0 - 64 бит
Я даже попробовал стандартный JDBC API без использования конкретного Oracle (как в примере выше) безуспешно.
Это намного проще:
PreparedStatement pstmt =
conn.prepareStatement("update blob_table set blob = ? where id = ?");
File blob = new File("/path/to/picture.png");
FileInputStream in = new FileInputStream(blob);
// the cast to int is necessary because with JDBC 4 there is
// also a version of this method with a (int, long)
// but that is not implemented by Oracle
pstmt.setBinaryStream(1, in, (int)blob.length());
pstmt.setInt(2, 42); // set the PK value
pstmt.executeUpdate();
conn.commit();
pstmt.close();
Он работает так же, когда используется оператор INSERT. Нет необходимости в empty_blob()
и втором обновлении.
FWIW, для чего-то, что умещается в памяти, я обнаружил, что могу просто передать байтовый массив в качестве подготовленного параметра оператора, вместо того, чтобы проходить через «строгий» моральный суровый настрой (или, что хуже, специфичные для Oracle / предлагаемые вещи)
Используя оболочку Spring «JDBC template» (org.springframework.jdbc.core.JdbcTemplate), чтобы поместить содержимое «большой» (или нет) строки в столбец BLOB, код выглядит примерно так:
jdbc.update( "insert into a_table ( clob_col ) values ( ? )", largeStr.getBytes() );
Нет шага 2.