Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Overcomplicated oracle jdbc BLOB handling

When I search the web for inserting BLOBs into Oracle database with jdbc thin driver, most of the webpages suggest a 3-step approach:

  1. insert empty_blob() value.
  2. select the row with for update.
  3. insert the real value.

This works fine for me, here is an example:

Connection oracleConnection = ...  byte[] testArray = ...  PreparedStatement ps = oracleConnection.prepareStatement(     "insert into test (id, blobfield) values(?, empty_blob())"); ps.setInt(1, 100); ps.executeUpdate(); ps.close(); ps = oracleConnection.prepareStatement(     "select blobfield from test where id = ? for update"); ps.setInt(1, 100); OracleResultSet rs = (OracleResultSet) ps.executeQuery(); if (rs.next()) {     BLOB blob = (BLOB) rs.getBLOB(1);     OutputStream outputStream = blob.setBinaryStream(0L);     InputStream inputStream = new ByteArrayInputStream(testArray);     byte[] buffer = new byte[blob.getBufferSize()];     int byteread = 0;     while ((byteread = inputStream.read(buffer)) != -1) {         outputStream.write(buffer, 0, byteread);     }     outputStream.close();     inputStream.close(); } 

There are some webpages where the authors suggest using a simpler 1-step solution. Previous example with this solution:

Connection oracleConnection = ...  byte[] testArray = ...  PreparedStatement ps = oracleConnection.prepareStatement(     "insert into test(id, blobfield) values(?, ?)"); BLOB blob = BLOB.createTemporary(oracleConnection, false, BLOB.DURATION_SESSION); OutputStream outputStream = blob.setBinaryStream(0L); InputStream inputStream = new ByteArrayInputStream(testArray); byte[] buffer = new byte[blob.getBufferSize()]; int byteread = 0; while ((byteread = inputStream.read(buffer)) != -1) {     outputStream.write(buffer, 0, byteread); } outputStream.close(); inputStream.close();  ps.setInt(1, 100); ps.setBlob(2, blob); ps.executeUpdate(); ps.close(); 

The second code is much more easier, so my question is: What is the point of first (popular) solution? Is there (was there) some kind of constraint for the second solution (Oracle server version number, jdbc driver version, size of the blob,...)? Is the first solution better (speed, memory consumption,...)? Any reasons for not using the simpler second approach?

The exact same question applies for CLOB fields.

like image 896
asalamon74 Avatar asked May 14 '09 09:05

asalamon74


People also ask

Does Oracle support blob?

Oracle blob storage requires usinf the Oracle dbms_lob package an easy interface tio Oracle BLOB storage. The LOB storage clause is not needed if the maximum size of the BLOB doesn't exceed 4000 bytes. Up to 4000 bytes can be stored in-line with the other data in the tablespace.

What is blob in JDBC?

An SQL BLOB is a built-in type that stores a Binary Large Object as a column value in a row of a database table. By default drivers implement Blob using an SQL locator(BLOB) , which means that a Blob object contains a logical pointer to the SQL BLOB data rather than the data itself.

What is BLOB datatype in Oracle with example?

A BLOB (binary large object) is a varying-length binary string that can be up to 2,147,483,647 characters long. Like other binary types, BLOB strings are not associated with a code page. In addition, BLOB strings do not hold character data.

Why blob is used in Oracle?

Binary Large Objects (BLOB) support is designed for efficient storage of large objects. An object is considered to be large if it is more than a third of the size of a page.


2 Answers

The update approach you mention in the first case can be rewritten using pure JDBC code and thus reduce your dependency on Oracle-specific classes. This could be helpful if your app needs to be database agnostic.

public static void updateBlobColumn(Connection con, String table, String blobColumn, byte[] inputBytes, String idColumn, Long id) throws SQLException {   PreparedStatement pStmt = null;   ResultSet rs = null;   try {     String sql =        " SELECT " + blobColumn +        " FROM " + table +        " WHERE " + idColumn + " = ? " +       " FOR UPDATE";     pStmt = con.prepareStatement(sql,        ResultSet.TYPE_FORWARD_ONLY,        ResultSet.CONCUR_UPDATABLE);     pStmt.setLong(1, id);     rs = pStmt.executeQuery();     if (rs.next()) {       Blob blob = rs.getBlob(blobColumn);       blob.truncate(0);       blob.setBytes(1, inputBytes);       rs.updateBlob(blobColumn, blob);       rs.updateRow();     }   }   finally {     if(rs != null) rs.close();     if(pStmt != null) pStmt.close();   } } 

For MSSQL I understand that the locking syntax is different:

String sql =    " SELECT " + blobColumn +    " FROM " + table + " WITH (rowlock, updlock) " +    " WHERE " + idColumn + " = ? " 
like image 190
Mr. Shiny and New 安宇 Avatar answered Sep 19 '22 23:09

Mr. Shiny and New 安宇


Another point of view from Oracle DBA. Sun guys did very poor job when they designed JDBC standards(1.0, 2.0, 3.0, 4.0). BLOB stands for large object and therefore it can be very large. It is something that can not be stored in JVM heap. Oracle thinks of BLOBs as something like file handles(it fact they are call then "lob locators"). LOBS can not be created via constructor and are not Java objects. Also LOB locators(oracle.sql.BLOB) can not be created via constructor - they MUST be created in the DB side. In Oracle there are two ways how to create a LOB.

  1. DBMS_LOB.CREATETEMPORATY - the returned locator in this case points into temporary tablespace. All the writes/reads against this locator will be sent via network onto DB server. Nothing is stored in JVM heap.

  2. Call to EMPTY_BLOB function. INSERT INTO T1(NAME, FILE) VALUES("a.avi", EMPTY_BLOB()) RETURNING FILE INTO ?; In this case returned lob locator points into data tablespace. All the writes/reads against this locator will be sent via network onto DB server. All the writes are "guarded" by writes into redo-logs. Nothing is stored in JVM heap. The returning clause was not supported by JDBC standards (1.0, 2.0), therefore you can find many examples on the internet where people recommend approach of two steps: "INSERT...; SELECT ... FOR UPDATE;"

Oracle lobs must be associated with some database connection, they can not be used when DB connection is lost/closed/(or "commited"). They can not be passed from one connection to another.

You second example can work, but will require excessive copying if data from temporary tablespace into data tablespace.

like image 20
Ivan Avatar answered Sep 21 '22 23:09

Ivan