Search code examples
javaoracle-databasestored-proceduresblob

Oracle Store Procedure with BLOB parameter


Hi i'm new working with this technologies (Oracle SP ), so I have some problems with it,

To be specific I want to insert a BLOB object over a Store Procedure, currently I work with spring, jboss, java and oracle, my SP is simple than :

PROCEDURE SAVE_DATA(data IN BLOB, date IN DATE) IS
next_id number;
BEGIN
  select s_id.nextval into next_id from dual;

  INSERT INTO DATA_TABLE( id, data , date)
  values
  (next_id, data , date);
  COMMIT;

  EXCEPTION
   WHEN OTHERS THEN
     RAISE_APPLICATION_ERROR(-20101,''||SQLCODE ||'-'||SUBSTR(SQLERRM,1,500));
  END SAVE_FAILED_EMAIL;

So in the java side, I do something like this:

  WrappedConnection wrappedCon = (WrappedConnection) this.getDataSource().getConnection();
        con = (OracleConnection) wrappedCon.getUnderlyingConnection();
        byte[] bytes= IOUtils.toByteArray(input);
        blobObj=con.createBlob(bytes);

  execute(new CallableStatementCreator() {

        public CallableStatement createCallableStatement(Connection con)
                throws SQLException {
             String procedure = "call SAVE_DATA(?,?)";

                CallableStatement stm=con.prepareCall(procedure);

                stm.setBlob(1, blobObj);
                stm.setDate(2, date);
            return stm;
        }
    }, new CallableStatementCallback<Map<Integer,Object>>() {

        public Map<Integer, Object> doInCallableStatement(CallableStatement cs) 
        throws SQLException,DataAccessException {
            cs.execute();
            return null;
        }} 
    );
    con.commit();
    con.close();

But when I run this part of the code I get the next exception that comes form DB side "ORA-22927 invalid LOB locator specified"


Solution

  • This one is a little bit tricky. The first problem you got here is that Oracle needs a propietary BLOB and CLOB instances; here is the javadoc from Spring OracleLobHandler:

    While most databases are able to work with DefaultLobHandler, Oracle 9i (or more specifically, the Oracle 9i JDBC driver) just accepts Blob/Clob instances created via its own proprietary BLOB/CLOB API, and additionally doesn't accept large streams for PreparedStatement's corresponding setter methods.

    But as you are working in JBoss you will also need a NativeJdbcExtractor so Spring can unwrap the underlying connection from JBoss thread pool wrapper and then insert the lob in Spring JdbcTemplate.

    So, here is the code you need to change:

    // ...
    final byte[] bytes= IOUtils.toByteArray(input);
    
    final OracleLobHandler lobHandler = new OracleLobHandler();
    final lobHandler.setNativeJdbcExtractor(new JBossNativeJdbcExtractor());
    // ...
    new CallableStatementCreator() {
    
        public CallableStatement createCallableStatement(Connection con)
                throws SQLException {
             String procedure = "call SAVE_DATA(?,?)";
    
                CallableStatement stm=con.prepareCall(procedure);
    
                lobHandler.getLobCreator().setLobAsBytes(smt, 1, bytes, bytes.length);
                stm.setDate(2, date);
            return stm;
        }
    }
    // ...