4

This is my first time posting a question in stackoverflow, I need to write a java stored procedure that creates an excel file and returns a blob containing the file data in bytes.

My pl/sql function is in the following form

function test_create_excel(i_username IN varchar2) return BLOB
   AS LANGUAGE JAVA NAME 'NTO.Excel.ExcelFunctions.PushToExcel( java.lang.String ) return java.sql.Blob';

my Java method is as follows

public static java.sql.Blob TestPushToExcel(String username) throws IOException, SQLException{
        //create excel file, read content to byte array and set to a blob
    }

My problem is that I cannot find any way to create an instance of java.sql.Blob so that i can use the blob.setBinaryStream(..) method to write the file data byte array.

I tried to use the SerialBlob implementation but it results in the following oracle error

ORA-00932: inconsistent datatypes: expected a return value that is an instance of a user defined Java class convertible to an Oracle type got an object that could not be converted

has anyone come across this issue and if so can you share on how you got through it.

Thank you in Advance.

EDIT JAVA

public static oracle.sql.BLOB getBlob(byte[] data) throws SQLException, IOException{
        oracle.jdbc.OracleConnection conn = (oracle.jdbc.OracleConnection)new OracleDriver().defaultConnection();

        oracle.sql.BLOB retBlob = oracle.sql.BLOB.createTemporary(conn, true, oracle.sql.BLOB.DURATION_SESSION);

        java.io.OutputStream outStr = retBlob.setBinaryStream(0);
        outStr.write(data);
        outStr.flush();

        return retBlob;
  }


public static ExcelFileStore PushToExcel(String userId) throws IOException, SQLException{

        ExcelFileStore fileStore = new ExcelFileStore();        
        fileStore.NU_USERID = userId;
        fileStore.CreatedTime = new java.sql.Date(new Date().getTime());
        fileStore.Last_Updated = new java.sql.Date(new Date().getTime());        
        fileStore.FileSize = fileData.length;
        fileStore.FileData = getBlob(fileData);        
        return fileStore;
    }

PL/SQL

 function test_create_excel(i_username IN varchar2) return EXCELFILESTORE    AS LANGUAGE JAVA NAME 'NTO.Excel.ExcelFunctions.PushToExcel( java.lang.String, ) return OracleObjects.ExcelFileStore';

OracleObject.ExcelfileStore is a class that implements java.sql.SqlData and EXCELFILESTORE is a UDT in oracle.

I loaded the reference jars and jar created for my code using 'sys.dbms_java.loadjava'

I hope you understand my question, as i'm quite new to pl/sql programming

harindya
  • 93
  • 1
  • 3
  • 10
  • You can't use data type with oracle, only supported datatypes work. –  Nov 19 '13 at 13:12
  • its a mistake in my part, when pasting my code here, the method does not return ExcelFileStore. I edited my question, sorry about that. – harindya Nov 19 '13 at 13:38

1 Answers1

7

I was wrong. It can be done. Took me a while to get it to work, but, finally, here is a working example:

Java class

import oracle.jdbc.driver.*;

public class TestBlob {
  public static oracle.sql.BLOB getBlob(String username) throws Exception {
    oracle.jdbc.OracleConnection conn = 
      (oracle.jdbc.OracleConnection)new OracleDriver().defaultConnection();

    oracle.sql.BLOB retBlob =
      oracle.sql.BLOB.createTemporary(conn,
                                      true,
                                      oracle.sql.BLOB.DURATION_SESSION);

    java.io.OutputStream outStr = retBlob.setBinaryStream(0);
    outStr.write(username.getBytes());
    outStr.flush();

    return retBlob;
  }
}

As you can see, I have used the oracle.sql.BLOB for the result. I create it with the static createTemporary method of the BLOB class, specifying that it should be created for the duration of the session (oracle.sql.BLOB.DURATION_SESSION parameter).

Then, I obtain the OutputStream and write the data. Flush was needed.

Database side

create or replace FUNCTION getBlobWrp (username IN VARCHAR2) RETURN BLOB
  AS LANGUAGE JAVA NAME
              'TestBlob.getBlob(java.lang.String) return oracle.sql.BLOB';

Test:

DECLARE
  l_blob BLOB;
BEGIN
  l_blob := getBlobWrp('test');

  dbms_output.put_line(UTL_RAW.CAST_TO_VARCHAR2(l_blob));
END;

Output:

test

(previous answer)

I think you should have an IN OUT BLOB parameter in your test_create_excel function (change it to a procedure), and operate on that parameter inside your Java stored method. I saw that approach once.

Before calling the test_create_excel, you should create a BLOB object:

DECLARE
  l_blob BLOB;
BEGIN
  DBMS_LOB.createtemporary(l_blob, TRUE);
  test_create_excel('username', l_blob);
END;

Edit

I don't think what you're trying to do is possible. However, you could wrap the above code in another function. It's a bit messy, but then you'll have a function which returns the blob:

CREATE OR REPLACE FUNCTION get_excel_blob(p_username VARCHAR2) RETURN BLOB
AS
  l_blob BLOB;
BEGIN
  DBMS_LOB.createtemporary(l_blob, TRUE);
  test_create_excel(p_username, l_blob);
  RETURN l_blob;
END;
Przemyslaw Kruglej
  • 8,003
  • 2
  • 26
  • 41
  • Thank you, I did try an approach similar to this by creating a temp blob in pl/sql and passing it to the java method, but it would be more clean if i can create the blob in java itself. Thank you again – harindya Nov 19 '13 at 13:35
  • Thank you Przemyslaw, I guess i will use this approach. – harindya Nov 19 '13 at 14:44
  • @harindya I'm sorry for my first, misleading answer. It _can_ be done, the BLOB is mapped to `oracle.sql.BLOB` class. Please check my edited answer. – Przemyslaw Kruglej Nov 19 '13 at 15:19
  • Thank you very much again for your help, I tried your approach to create the blob in java, and everything builds fine, but when i ran the procedure in pl/sql i'm getting the following java exception Exception in thread "Root Thread" java.lang.IncompatibleClassChangeError at oracle.sql.BLOB.createTemporary(BLOB.java:671) at NTO.Excel.ExcelFunctions.getBlob(ExcelFunctions.java:66) at NTO.Excel.ExcelFunctions.PushToExcel(ExcelFunctions.java:169) do i need to load reference jar to oracle. I'm using jdk1.5. Do you have any idea why i'm getting this exception? – harindya Nov 20 '13 at 13:21
  • @harindya Could you please show the most important parts of your Java code (edit your question) and PL/SQL wrapper? How do you load the Java into database? – Przemyslaw Kruglej Nov 20 '13 at 13:25
  • 1
    @harindya The error is strange. What I would suggest: try my example, and see if it works for you. Also, check this thread for possible solutions to the error: [IncompatibleClassChangeError In Java](http://stackoverflow.com/questions/1980452/what-causes-java-lang-incompatibleclasschangeerror) I suggest that you remove all your Java related code from the database and the wrapper function (drop it all) and try importing it again. I don't think you should load the reference jar, unless you get an error that some class is missing. In my example, I didn't load any additional jars. – Przemyslaw Kruglej Nov 20 '13 at 13:56
  • i tried loading my jar to a fresh Oracle instance, and was able to create and return the blob from java to oracle. Many thanks for your help. – harindya Nov 20 '13 at 15:29