신뢰할 수 있는 방식으로 오라클 BLOB를 작성/업데이트하는 방법은 무엇입니까?
저는 blob 열에 pdf 문서를 작성하고 업데이트하려고 하지만 이전에 저장된 데이터보다 더 많은 데이터를 쓰기만 하면 blob을 업데이트할 수 있습니다.더 작은 문서 데이터로 블롭 열을 업데이트하려고 하면 손상된 PDF만 표시됩니다.
먼저 blob 열이 empty_blob() 함수를 사용하여 초기화되었습니다.저는 이 동작을 테스트하기 위해 아래 샘플 자바 클래스를 작성했습니다.첫 번째로 'true'를 주 메서드의 첫 번째 매개 변수로 사용하여 실행하므로 첫 번째 행에는 약 31kB의 문서가 저장되고 두 번째 행에는 278kB의 문서가 저장됩니다.그런 다음 '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비트
저는 심지어 Oracle의 특정 JDBC API를 사용하지 않고도 표준 JDBC API를 시도했지만 성공하지 못했습니다.
훨씬 쉽습니다.
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()
그리고 두 번째 업데이트 문.
준비된 진술에 의존하는 a_horse_with_no_name의 답변에 추가합니다.setBinaryStream(...) API), BLOB에는 적어도 두 가지 이상의 옵션이 있고 CLOB 및 NCLOB에는 세 가지 이상의 옵션이 있습니다.
명시적으로 LOB를 생성하고, LOB에 쓰고, 사용
PreparedStatement.setBlob(int, Blob)
:int insertBlobViaSetBlob(final Connection conn, final String tableName, final int id, final byte value[]) throws SQLException, IOException { try (final PreparedStatement pstmt = conn.prepareStatement(String.format("INSERT INTO %s (ID, VALUE) VALUES (?, ?)", tableName))) { final Blob blob = conn.createBlob(); try (final OutputStream out = new BufferedOutputStream(blob.setBinaryStream(1L))) { out.write(value); } pstmt.setInt(1, id); pstmt.setBlob(2, blob); return pstmt.executeUpdate(); } }
빈 LOB 업데이트(를 통해 삽입)
DBMS_LOB.EMPTY_BLOB()
또는DBMS_LOB.EMPTY_CLOB()
) 경유SELECT ... FOR UPDATE
이는 Oracle 관련 사항이므로 하나의 문 대신 두 개의 문을 실행해야 합니다.게다가, 이것은 당신이 애초에 성취하려고 했던 것입니다.void insertBlobViaSelectForUpdate(final Connection conn, final String tableName, final int id, final byte value[]) throws SQLException, IOException { try (final PreparedStatement pstmt = conn.prepareStatement(String.format("INSERT INTO %s (ID, VALUE) VALUES (?, EMPTY_BLOB())", tableName))) { pstmt.setInt(1, id); pstmt.executeUpdate(); } try (final PreparedStatement pstmt = conn.prepareStatement(String.format("SELECT VALUE FROM %s WHERE ID = ? FOR UPDATE", tableName))) { pstmt.setInt(1, id); try (final ResultSet rset = pstmt.executeQuery()) { while (rset.next()) { final Blob blob = rset.getBlob(1); try (final OutputStream out = new BufferedOutputStream(blob.setBinaryStream(1L))) { out.write(value); } } } } }
CLOB 및 NCLOB의 경우 추가로
PreparedStatement.setString()
그리고.setNString()
,각각 다음과 같다.
FWIW, 메모리에 맞는 것에 대해 "스트림" 엄격한 사기(또는 더 나쁜 Oracle 특정/제안 사항)를 거치지 않고 단순히 바이트 배열을 준비된 문 매개 변수로 전달할 수 있다는 것을 알게 되었습니다.
Spring "JDBC template" 래퍼(org.springframework.jdbc.core) 사용.JdbcTemplate)는 "큰"(또는 그렇지 않은) 문자열의 내용을 BLOB 열에 넣는 코드로, 다음과 같습니다.
jdbc.update( "insert into a_table ( clob_col ) values ( ? )", largeStr.getBytes() );
2단계는 없습니다.
언급URL : https://stackoverflow.com/questions/8348427/how-to-write-update-oracle-blob-in-a-reliable-way
'programing' 카테고리의 다른 글
IIS7에서 작동하는 gzip 압축을 어떻게 얻을 수 있습니까? (0) | 2023.06.21 |
---|---|
Nodejs 애플리케이션 오류: pm2 배포를 사용할 때 EADDRINUSE 바인딩 (0) | 2023.06.21 |
ASP.NET 사용자 정의 404를 찾을 수 없음 404 대신 200 OK 반환 (0) | 2023.06.21 |
절대 URL과 상대 URL에 가입하는 방법은 무엇입니까? (0) | 2023.06.21 |
C#.NET 응용 프로그램에서 SQL Server 시간 데이터 유형을 사용하시겠습니까? (0) | 2023.06.21 |