4

I am running some procedure from groovy:

    sql.call("{call SCHEMA.NAME_PROCEDURE($par1,$par2,$par3)}"){}

where sql is instance of database connection

this works fine.

now I need to parametrize SCHEMA so I try something like this:

    sql.call("{call ${schema}.NAME_PROCEDURE($par1,$par2,$par3)}"){}

or

    sql.call("{call " + schema + ".NAME_PROCEDURE($par1,$par2,$par3)}"){}

but with no success. I have no idea why this two fragment of code doesnt work. There is still some sqlException. What I am doing wrong ?

please help

EDIT:

I found similar question but still with no answer:

http://groovy.329449.n5.nabble.com/Calling-stored-procedures-td344943.html

hudi
  • 15,555
  • 47
  • 142
  • 246
  • You're going to get better feedback if you display the actual error. "It doesn't work" is rarely useful. – billjamesdev Feb 04 '13 at 16:47
  • there is no `call()` method on `java.sql.Connection`. – jtahlborn Feb 04 '13 at 16:50
  • @jtahlborn sure there is such method if first example works. sql=Sql.newInstance... – hudi Feb 04 '13 at 19:33
  • @BillJames error is that it cant find function – hudi Feb 04 '13 at 19:34
  • I vaguely recall getting an error like that in Groovy the turned out to be a problem with the stored procedure not being present on the database side. Please text grab the actual error and put it in your question so it is clear which system is complaining about not finding a procedure. – Mel Nicholson Feb 04 '13 at 23:44
  • PLS-00487: Invalid reference to variable '1' ORA-06550: line 1, column 7: PL/SQL: Statement ignored java.sql.SQLException: ORA-06550: line 1, column 27: PLS-00487: Invalid reference to variable '1' ORA-06550: line 1, column 7: PL/SQL: Statement ignored I dont understand how this helps you. I said there is some syntax error – hudi Feb 05 '13 at 07:24
  • I am not that good with Groovy, but shouldn't `{$schema}` be `${schema}` – Incognito Feb 05 '13 at 09:08
  • yes you are right copy/paste error. I edit question – hudi Feb 05 '13 at 09:32
  • Can you try this: `sql.call("{call "$schema".NAME_PROCEDURE($par1,$par2,$par3)}"){}` – Incognito Feb 05 '13 at 12:01
  • then groovy is not compiled: 65: unexpected token: $schema @ line 65, column 30. sql.call("{call "$schema"... – hudi Feb 05 '13 at 12:13

4 Answers4

2

To be more explicit than the answer from @mtk above, try changing:

sql.call("{call " + schema + ".NAME_PROCEDURE($par1,$par2,$par3)}"){}

to:

sql.call(GString.EMPTY + "{call " + schema + ".NAME_PROCEDURE($par1,$par2,$par3)}"){}

You first attempt will not work - this is attempting to bind the name of the procedure, it will generate SQL of the form:

 { call ?.NAME_PROCEDURE(?,?,?) }

The second one is slightly less obvious. Groovy SQL uses the GString object to generate SQL and a bindlist. However, because you start with a raw string, the result of the expression will be a raw string, so what is passed to sql.call will look like:

 { call schema.NAME_PROCEDURE(par1,par2,par2) }

Not:

 { call schema.NAME_PROCEDURE(${par1},${par2},${par3}) }

which is what you really want. If par1-3 are all numbers, you would be OK with this, but if they are Strings (or some other type that would be coerced to a string by replacement), this probably isn't valid SQL, hence your SQL exception.

Basically String + GString = String. Groovy SQL is expecting a GString instance so that it can set up the bind list correctly for this query.

You can get around this by forcing the string to be a 'GString' instance. GString is defined such that GString + String = GString. You can see this at the groovy console:

groovy> def par1 = 1 
groovy> def par2 = 2 
groovy> def par3 = 3 
groovy> def schema = 'myschema' 
groovy> println (("{call " + schema + ".NAME_PROCEDURE($par1,$par2,$par3)}").class) 
groovy> println ((GString.EMPTY + "{call " + schema + ".NAME_PROCEDURE($par1,$par2,$par3)}").class) 

class java.lang.String
class groovy.lang.GString$2

By coercing the "{call" to be a GString instance, this will then cascade along the 'plus' invocations, so you can ensure that Groovy SQL gets the input it needs to create the 'correct' bind list/SQL.

Peter Hart
  • 4,955
  • 2
  • 25
  • 30
1

I am not well versed with this, but just digging through the documentation this is what I see, and other possibilities that may occur -

  1. The parameter expects to be a GString which is not same as String. Hope it does get converted, but trying explicitly by composing a string with variables and later convert it to a GString object as [GString info]

    GString g = GString.EMPTY + normal_str_variable; 
    
  2. The schema variable is not set with proper value.

  3. The Sql Instance might be closed and which you would not have checked properly.

mtk
  • 13,221
  • 16
  • 72
  • 112
0

Hope its a Duplicate of the question Calling stored procedure from Java / JPA

But my answer is: See code snippets

//getDBUSERByUserId is a stored procedure
String getDBUSERByUserIdSql = "{call getDBUSERByUserId(?,?,?,?)}";
callableStatement = dbConnection.prepareCall(getDBUSERByUserIdSql);
callableStatement.setInt(1, 10);
callableStatement.registerOutParameter(2, java.sql.Types.VARCHAR);
callableStatement.registerOutParameter(3, java.sql.Types.VARCHAR);
callableStatement.registerOutParameter(4, java.sql.Types.DATE);

// execute getDBUSERByUserId store procedure
callableStatement.executeUpdate();

String userName = callableStatement.getString(2);
String createdBy = callableStatement.getString(3);
Date createdDate = callableStatement.getDate(4);

Example code:

Stored Procedure:

CREATE OR REPLACE PROCEDURE getDBUSERByUserId(
       p_userid IN DBUSER.USER_ID%TYPE,
       o_username OUT DBUSER.USERNAME%TYPE,
       o_createdby OUT  DBUSER.CREATED_BY%TYPE,
       o_date OUT DBUSER.CREATED_DATE%TYPE)
IS
BEGIN

  SELECT USERNAME , CREATED_BY, CREATED_DATE
  INTO o_username, o_createdby,  o_date 
  FROM  DBUSER WHERE USER_ID = p_userid;

END;

Calls Stored Procedure via CallableStatement

public class JDBCCallableStatementOUTParameterExample {

    private static final String DB_DRIVER = "oracle.jdbc.driver.OracleDriver";
    private static final String DB_CONNECTION = "jdbc:oracle:thin:@localhost:1521:MKYONG";
    private static final String DB_USER = "user";
    private static final String DB_PASSWORD = "password";

    public static void main(String[] argv) {
        try {
            callOracleStoredProcOUTParameter();
        } catch (SQLException e) {
            System.out.println(e.getMessage());
        }
    }
    private static void callOracleStoredProcOUTParameter() throws SQLException {
        Connection dbConnection = null;
        CallableStatement callableStatement = null;
        String getDBUSERByUserIdSql = "{call getDBUSERByUserId(?,?,?,?)}";
        try {
            dbConnection = getDBConnection();
            callableStatement = dbConnection.prepareCall(getDBUSERByUserIdSql);
            callableStatement.setInt(1, 10);
            callableStatement.registerOutParameter(2, java.sql.Types.VARCHAR);
            callableStatement.registerOutParameter(3, java.sql.Types.VARCHAR);
            callableStatement.registerOutParameter(4, java.sql.Types.DATE);
            // execute getDBUSERByUserId store procedure
            callableStatement.executeUpdate();
            String userName = callableStatement.getString(2);
            String createdBy = callableStatement.getString(3);
            Date createdDate = callableStatement.getDate(4);
            System.out.println("UserName : " + userName);
            System.out.println("CreatedBy : " + createdBy);
            System.out.println("CreatedDate : " + createdDate);
        } catch (SQLException e) {
            System.out.println(e.getMessage());
        } finally {
            if (callableStatement != null) {
                callableStatement.close();
            }
            if (dbConnection != null) {
                dbConnection.close();
            }
        }
    }
    private static Connection getDBConnection() {
        Connection dbConnection = null;
        try {
            Class.forName(DB_DRIVER);
        } catch (ClassNotFoundException e) {
            System.out.println(e.getMessage());
        }
        try {
            dbConnection = DriverManager.getConnection(
                DB_CONNECTION, DB_USER,DB_PASSWORD);
            return dbConnection;
        } catch (SQLException e) {
            System.out.println(e.getMessage());
        }
        return dbConnection;
    }
 }

Hope this may help you. Thanks.

Community
  • 1
  • 1
A Programmer
  • 368
  • 4
  • 7
  • 26
0

The question asks for groovy solution, so vanilla java is off the mark. Here is a solution that uses "inline procedure" for better parameter handling. There is also a sql.callWithRows and sql.callWithAllRows available after grails 2.4

    def calculateTotals(map) {
    //initialize variables
    Double returnTotalOriginalOut = 0
    Double returnTotalOtherOut = 0
    Double returnTotalNetOut = 0

    def sql = new Sql(sessionFactory.currentSession.connection())
    //calculate the totals
    sql.call("""
             DECLARE
                return_orig_chgs      number := 0;
                return_non_orig_chgs  number := 0;
                return_net_inst_chgs  number := 0;
             BEGIN
               SCHEMA.NAME_PROCEDURE(id         => ${map.id},
                                     term_in        => ${map.term},
                                     orig_chgs      => return_orig_chgs,
                                     non_orig_chgs  => return_non_orig_chgs,
                                     net_inst_chgs  => return_net_inst_chgs);
             ${Sql.DOUBLE} := return_orig_chgs;
             ${Sql.DOUBLE} := return_non_orig_chgs;
             ${Sql.DOUBLE} := return_net_inst_chgs;
            END ;
    """) { return_orig_chgs, return_non_orig_chgs, return_net_inst_chgs ->
        returnTotalOriginalOut = return_orig_chgs
        returnTotalOtherOut = return_non_orig_chgs
        returnTotalNetOut = return_net_inst_chgs
    }

    def returnMap = [:]
    returnMap = [returnTotalOriginal: returnTotalOriginalOut, returnTotalOther: returnTotalOtherOut, returnTotalNet: returnTotalNetOut]
    return returnMap
}
Duane5000
  • 33
  • 6