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:

  1. 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();
        }
    }
    
  2. Update an empty LOB (inserted via DBMS_LOB.EMPTY_BLOB() or DBMS_LOB.EMPTY_CLOB()) via SELECT ... 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);
                    }
                }
            }
        }
    }
    
  3. For CLOBs and NCLOBs, you can additionally use PreparedStatement.setString() and setNString(), 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.