How to write / update Oracle blob in a reliable way?
It's a lot easier:
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();
It works the same when using an INSERT statement. No need for empty_blob()
and a second update statement.
In addition to a_horse_with_no_name's answer (which relies on PreparedStatement.setBinaryStream(...) API), there're at least two more options for BLOBs, and 3 more for CLOBs and NCLOBs:
Explicitly create a LOB, write to it, and use
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(); } }
Update an empty LOB (inserted via
DBMS_LOB.EMPTY_BLOB()
orDBMS_LOB.EMPTY_CLOB()
) viaSELECT ... FOR UPDATE
. This is Oracle-specific and requires two statements executed instead of one. Additionally, this is what you were trying to accomplish in the first place: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); } } } } }
For CLOBs and NCLOBs, you can additionally use
PreparedStatement.setString()
andsetNString()
, respectively.
FWIW, for something that fits in memory, I found I could simply pass in a byte array as the prepared statement parameter, rather than going through the "stream" rigor morale (or worse Oracle specific/suggested things)
Using a Spring "JDBC template" wrapper (org.springframework.jdbc.core.JdbcTemplate) to put the contents of a "large" (or not) string into a BLOB column, the code is something like the following:
jdbc.update( "insert into a_table ( clob_col ) values ( ? )", largeStr.getBytes() );
There is no step 2.