12

I'm using a Groovy script in Mule ESB to get output parameters from Oracle stored procedure (including cursor) and getting an exception.

Minimal example:

import groovy.sql.Sql
import oracle.jdbc.pool.OracleDataSource
import oracle.jdbc.driver.OracleTypes

def ds = new OracleDataSource()
// setting data source parameters here

def sql = new Sql(ds)
def data = []

sql.call("""declare
result_table sys_refcursor;
begin

open result_table for select 1 as a from dual;

insert into CURSOR_TEST (ID) values (1);
commit;

${Sql.resultSet OracleTypes.CURSOR} := result_table;

insert into CURSOR_TEST (ID) values (2);
commit;

end;
"""
){ table ->

  throw new RuntimeException("Never getting this exception.")

  table.eachRow {
    data << it.toRowResult()
  }
}

sql.close()

return data

Error:


Message               : java.sql.SQLException: Closed Statement (javax.script.ScriptException)
Code                  : MULE_ERROR--2
--------------------------------------------------------------------------------
Exception stack is:
1. Closed Statement(SQL Code: 17009, SQL State: + 99999) (java.sql.SQLException)
  oracle.jdbc.driver.SQLStateMapping:70 (null)
2. java.sql.SQLException: Closed Statement (javax.script.ScriptException)
  org.codehaus.groovy.jsr223.GroovyScriptEngineImpl:323 (http://java.sun.com/j2ee/sdk_1.3/techdocs/api/javax/script/ScriptException.html)
3. java.sql.SQLException: Closed Statement (javax.script.ScriptException)

(org.mule.api.transformer.TransformerException) org.mule.module.scripting.transformer.ScriptTransformer:39 (http://www.mulesoft.org/docs/site/current3/apidocs/org/mule/api/transformer/TransformerException.html) -------------------------------------------------------------------------------- Root Exception stack trace: java.sql.SQLException: Closed Statement at oracle.jdbc.driver.SQLStateMapping.newSQLException(SQLStateMapping.java:70) at oracle.jdbc.driver.DatabaseError.newSQLException(DatabaseError.java:133) at oracle.jdbc.driver.DatabaseError.throwSqlException(DatabaseError.java:199) + 3 more (set debug level logging or '-Dmule.verbose.exceptions=true' for everything) ********************************************************************************

Select from CURSOR_TEST returns 1 and 2.

Oracle server version: Oracle Database 11g Enterprise Edition Release 11.2.0.3.0 - 64bit Production.

Mule version: 3.5.0.

I'm using jdbc\lib\ojdbc6.jar from oracle client version 11.1.0.7.0.

What am I doing wrong?

Ameya Deshpande
  • 3,580
  • 4
  • 30
  • 46
senia
  • 37,745
  • 4
  • 88
  • 129
  • 1
    Did you try to run that code in a Groovy script outside Mule? – Ale Sequeira May 06 '15 at 12:51
  • @senia in a deleted comment, you stated that you solved this issue by using a Java implementation instead. Can you please post this Java code as an answer to your question and accept it? It will also give us a chance to find out what was wrong with this Groovy implementation. – David Dossot May 26 '15 at 21:02
  • 1
    @DavidDossot: here is [my workaround example](http://pastebin.com/XWAzXCU5). I don't think this is a good answer to my question, just a good old java with a little groovy help. – senia May 27 '15 at 06:21
  • Thanks for sharing. I have the feeling that all this could be done in Groovy only, as these two Java static helpers don't _seem_ to do things that Groovy couldn't do. – David Dossot May 27 '15 at 06:28
  • 1
    @DavidDossot: I have to reuse DatabaseHelper in many groove scripts. Java class was the easiest way for me. Actually after fail with native groove instruments I've planned to drop groove and use generic java methods (with generic types instead of Object). Main problem here is not java vs groovy code. It's just not groovy way for me: there is no string interpolation help, I have to take care about parameters order. `extractResultSetAndClose` also looks ugly. – senia May 27 '15 at 06:46
  • Thank you for this extra info! – David Dossot May 27 '15 at 15:54

1 Answers1

2

The following code can help you get variable of SYS_REFCURSOR from Oracle anonymous block.

We should focus on a few key details:

  1. Class groovy.sql.Sql doesn't have corresponding OutParameter and we make it manually as CURSOR_PARAMETER and pass it to sql.call method
  2. Consider that the block starts with {call DECLARE and ends with END } without semicolon after END. Otherwise we can get a poorly recognizable SQLException in the face.
  3. The question marks ? inside the sqlString are places for parameter bindings. Bindings are made in the natural order. In this example:
    • the first ? binds with the first element in parametersList: "abc", treating the value as IN parameter ;
    • the second ? binds with CURSOR_PARAMETER treating the value as OUT parameter of passed type;
  4. There is only one enter into closure after sql.call and ResultSet rs provide rows of cursor my_cur declared in anonymous block.

import groovy.sql.OutParameter
import groovy.sql.Sql
import oracle.jdbc.OracleTypes

import java.sql.ResultSet

def driver = 'oracle.jdbc.driver.OracleDriver'
def sql = Sql.newInstance('jdbc:oracle:thin:@MY-SERVER:1521:XXX', 'usr', 'psw', driver)

// special OutParameter for cursor type
OutParameter CURSOR_PARAMETER = new OutParameter() {
    public int getType() {
        return OracleTypes.CURSOR;
    }
};

// look at some ceremonial wrappers around anonymous block
String sqlString = """{call
    DECLARE
      my_cur SYS_REFCURSOR;
      x VARCHAR2(32767) := ?;
    BEGIN

        OPEN my_cur
        FOR
        SELECT x || level AS my_column FROM dual CONNECT BY level < 10;

        ? := my_cur;
    END
}
""";

// the order of elements matches the order of bindings
def parametersList = ["abc", CURSOR_PARAMETER];


// rs contains the result set of cursor my_cur
sql.call(sqlString, parametersList) { ResultSet rs ->
  while (rs.next()) {
      println rs.getString("my_column")
  }
};
diziaq
  • 6,881
  • 16
  • 54
  • 96
  • Can we use string interpolation here? Without string interpolation it's not better than the [workaround](http://pastebin.com/XWAzXCU5). By the way, I can't test your solution. I have no access to Oracle DB instance. – senia Mar 02 '17 at 16:53
  • The key feature here is custom `OutParameter`, and other parts of solution are usual Groovy stuff. I showed only the shortest way to get the result you asked for. So, yes, we can use string interpolation. And I'm very surprised to hear you lack Oracle DB instance although the question is tagged `oracle`. What do you suppose me to do about that? – diziaq Mar 03 '17 at 06:03
  • Nothing. I mean: I can't accept your answer without testing. I'll try to test it next weekend. The question is outdated dramatically. It's more then 3 years old. – senia Mar 03 '17 at 06:28