how do i get the Store procedure response in java ?
when i run this in SQL i get a response
DECLARE
result NUMBER;
begin
result := apps.xx01_cpc_ap_pkg.xx01_create_invoice_f(8309, 4795, 146.00);
dbms_output.put_line(result);
end;
this is my java code
i tried many option - but i keep failing on how to fetch the response
in this part resultSet.getInt(4))
CallableStatement stmt = null;
String spCallRequest = "{? = call apps.xx01_cpc_ap_pkg.xx01_create_invoice_f(?, ?, ? )}";
stmt = oraAppUtils.connection.prepareCall(spCallRequest);
stmt.setString(1,paymentRequest.healthFacilityCode);
stmt.setString(2,paymentRequest.batchId);
stmt.setDouble(3,Double.parseDouble(paymentRequest.tariffAmount));
stmt.registerOutParameter(4, java.sql.Types.INTEGER);
resultSet = stmt.executeQuery();
System.out.println ("invoice id ="+resultSet.getInt(4));
ok - just for documentation in case some else needs it... this is the SP it has a function
CREATE OR REPLACE PACKAGE APPS.XX01_CPC_AP_PKG
AS
PROCEDURE XX01_CREATE_INVOICE (P_HF_CODE IN VARCHAR2,
P_BATCH_ID IN VARCHAR2,
P_AMOUNT IN NUMBER,
x_invoice_id OUT NUMBER/*,
x_error_code OUT VARCHAR2,
x_error_msg OUT VARCHAR2,
x_err_comments OUT VARCHAR2,
x_code OUT NUMBER,
x_source OUT VARCHAR2*/);
FUNCTION XX01_CREATE_INVOICE_F (P_HF_CODE IN VARCHAR2,
P_BATCH_ID IN VARCHAR2,
P_AMOUNT IN NUMBER) RETURN NUMBER;
so originaly i called the function - it didn't work - i change the code to the following code and it works
String spCallRequest = "BEGIN apps.xx01_cpc_ap_pkg.xx01_create_invoice(?, ?, ?,? ); END ; ";
stmt = oraAppUtils.connection.prepareCall(spCallRequest);
stmt.setString(1,paymentRequest.healthFacilityCode);
stmt.setString(2,paymentRequest.batchId);
stmt.setDouble(3,Double.parseDouble(paymentRequest.tariffAmount));
stmt.registerOutParameter(4, java.sql.Types.INTEGER);
stmt.execute();
System.out.println ("invoice id ="+stmt.getInt(4));
TL;DR
You did not call ResultSet#next()
Reason
From linked docs:
A ResultSet cursor is initially positioned before the first row; the
first call to the method next makes the first row the current row; the
second call makes the second row the current row, and so on.
Fix
Your code should look like this:
stmt.registerOutParameter(4, java.sql.Types.INTEGER);
resultSet = stmt.executeQuery();
if(resultSet.next()) {
System.out.println ("invoice id ="+resultSet.getInt(4));
} else {
//this should not happen
}
Your question didn't explicitly mention JDBC as the API that you must use, so perhaps, you're open to third party utilities that work on top of JDBC? For example, jOOQ's code generator can be used to generate stubs for all of your stored procedures. In your case, you could simply call the stub like this:
int result = Xx01CpcApPkg.xx01CreateInvoiceF(
configuration, // This object contains your JDBC Connection
paymentRequest.healthFacilityCode,
paymentRequest.batchId,
Double.parseDouble(paymentRequest.tariffAmount)
);
These objects are generated by jOOQ:
Xx01CpcApPkg: A class representing your PL/SQL package
xx01CreateInvoiceF(): A method representing your stored function
Behind the scenes, this does exactly what the accepted answer does, but:
You don't have to work with strings
You don't have to remember data types and parameter order
When you change the procedure in your database, and regenerate the code, then your client code stops compiling, so you'll notice the problem early on
Disclaimer: I work for the company behind jOOQ.
Related
I'm trying to run a stored procedure that returns a resultSet using oracle jdbc.
The procedure is as follows.
create or replace procedure display_players (rset OUT sys_refcursor)
as
Begin
open rset for select * from player_data;
End;
/
The java code is as follows
try {
sql = "{call display_players()}";
call = conn.prepareCall(sql);
call.execute();
rs = call.getResultSet();
while(rs.next()){
System.out.println(rs.getString("name") + " : " + rs.getString("club"));
}
I tried to register the out parameter as
call = conn.prepareCall("{call display_players(?)}");
call.registerOutParameter(1, OracleTypes.CURSOR);
But that dint work nor is the current code working as i get a null pointer exception which means the result set is not being returned.
how do i achieve this?
I think you haven't quite worked out how to get the result set from an OUT parameter from a stored procedure call.
Firstly, you need to register the OUT parameter, as in your second code sample:
call = conn.prepareCall("{call display_players(?)}");
call.registerOutParameter(1, OracleTypes.CURSOR);
However, once you've executed the statement, it's not correct to call.getResultSet() to get at the result set in the OUT parameter. For example, suppose you were calling a stored procedure that had two OUT parameters returning cursors. Which one should call.getResultSet() return?
The trick is to use call.getObject(...) to get the value of the parameter from call as an Object and then cast this to a ResultSet. In other words, replace the line
rs = call.getResultSet();
with
rs = (ResultSet)call.getObject(1);
I have a Stored Procedure that runs on MS SQL Server. It takes one parameter as input. Basically, it returns multiple rows.
I am calling the SP from my java application by using CallableStatement.
I would like to know if it possible to get the rows returned by the stored procedure in the form of ResultSet in my DAO layer?[like how we get a resultset when we do select * from EmployeeTable] . If yes, how do we do that?
P.S: I don’t have privilege to modify the stored procedure.
SQL Server knows two types of procedures returning results:
Batches
The procedure looks something like this:
CREATE PROCEDURE p_results(
#p_result_sets INT
)
AS
BEGIN
IF #p_result_sets = 1 BEGIN
SELECT 1 a;
END
ELSE IF #p_result_sets = 2 BEGIN
SELECT 1 a;
SELECT 1 b UNION SELECT 2 b;
END
END;
In this case, you don't know in advance what the result sets will look like, and how many of them you'll get. You will have to run the procedure using Statement.execute() as follows:
try (CallableStatement stmt = con.prepareCall("...")) {
boolean results = stmt.execute();
for (;;) {
if (results)
try (ResultSet rs = stmt.getResultSet()) {
// ... Fetch your results here
}
else if (stmt.getUpdateCount() != -1) {}
else
break;
results = stmt.getMoreResults();
}
// After all results are fetched, you can also retrieve OUT parameters, if applicable
}
Table-valued functions
The function looks something like this:
CREATE FUNCTION f_tables1 ()
RETURNS #out_table TABLE (
column_value INTEGER
)
AS
BEGIN
INSERT #out_table
VALUES (1)
RETURN
END
In this case, you don't really need a CallableStatement. An ordinary SELECT statement will do:
try (PreparedStatement stmt = con.prepareStatement("SELECT * FROM f_tables1()");
ResultSet rs = stmt.executeQuery()) {
// ... Fetch your results here
}
I made a function like ...
create or replace function
pile_data
(v_id IN NUMBER, v_area IN VARCHAR2, v_cust_id IN NUMBER)
return VARCHAR2
AS
rs VARCHAR2(2);
cur_id NUMBER;
-- PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
select if_seq.currVal into cur_id from dual;
select '00' into rs from dual;
insert into IF_WORK
(id, area, cust_id, rc)
values
(if_tx_seq.nextVal, v_area, v_cust_id, rs);
update IF_WORK set rc=rs where id = cur_id;
return rs;
exception
when dup_val_on_index then
select '01' into rs from dual;
return rs;
end;
And I got 2 questions
How to call this function as test?
Is it possible to return value in exception statement?
Follow code is what I want to implement(I made it with java, but have to implement with PLSQL)
public String pile_data(String ... params){
String rs = "00";
int cur_id = SEQ.currVal;
try{
insert(params);
}catch(Exception ex){
//HANDLING ERRORS
String exceptionName = ex.getClass().getName();
switch (exceptionName) {
case "KEY_DUPLICATION":
rs = "01";
break;
case "CONNECTION_TIMEOUT":
rs = "02";
break;
default:
rollback();
rs = "99";
break;
}
}finally{
// UPDATE ORIGINAL TABLE
update(cur_id, rs);
}
return rs;
}
My Conditions about the code.
Have to return anyway(SUCCESS OR NOT)
Have to update, whether insert success or not
I am sorry about java code, but this is what I can explain best. Thanks for answers. b
======== Edit
Sorry I did not told what I have failed. (Error Code from Oracle, message translated by myself)
execute pile_data(params ... );. it returns this error ORA-06550 : it is not a procedure or not defined
select pile_data(params) from dual;. it returns error ORA-14552: Can not execute DDL, Commit, Rollback which is inside of Query or DML
================= Edit #2 ================
So I am now trying to change this to a procedure with return value.
However, since caller need to get result, I wrapped with function.
create or replace procedure
pile_data
(params ... , rs OUT VARCHAR2)
IS
cur_id NUMBER;
BEGIN
select if_seq.currVal into cur_id from dual;
select '00' into rs from dual;
insert into IF_WORK
(target params ..., rc)
values
(params ..., rs);
update IF_WORK set rc=rs where tx_id = cur_id;
-- DBMS_OUTPUT.PUT_LINE(rs); -- it does not work
exception
when dup_val_on_index then
select '01' into rs from dual;
-- DBMS_OUTPUT.PUT_LINE(rs);
when others then
rollback;
select '99' into rs from dual;
-- DBMS_OUTPUT.PUT_LINE(rs);
end;
create or replace function pile_data_wrapper(params ...)
return varchar2
is
rs varchar2(2);
begin
pile_data(params ... , rs);
return rs;
end;
select pile_data_wrapper(params ... ) from dual;
And I got still ORA-14552.
I got a dilemma, a function cause an error and a procedure can not return.
I need better solution for it.
The goal I wanted is below
A remote DB will call my function, I need to return response code.
In this function I have to insert and update a table.
Thanks
Yes and yes :)
in order to test a function you can 1. save it as a database object by placing it in a package. or you can run it using the execute or exec keyword within your workbench (i usually do this in SQL developer)
(PWLSQL)you can always return a value in an exception block by using the return keyword.
(JAVA)you could write your own class that inherits from the exception class and write your own method that returns a certain value.
I hope this helps.
I can not say I solve this problem, but I found something. ORA-14552 error does not happen when I run this in sqlplus. That error only happens in sql developer.
follow is how I ran this in SQLPlus(terminal)
SQL> var tmp varchar2(2); -- define a variable
SQL> execute :tmp := PILE_DATA#DB_LINK('a', 1, 2); -- set value to variable
SQL> print tmp; -- print the result for checking
================================================
Other option is making a wrapper function which handle Exception.
i.e. Make a function without Exception handling like below.
FUNCTION CREATEWITH(v_id IN NUMBER,v_value1 IN VARCHAR2,v_value2 IN NUMBER)
return Number
Is
BEGIN
insert into DEVICE_BALJU(id, value1, value2)
values (v_id, v_value1, v_value2);
return SQL%ROWCOUNT ;
END CREATEWITH;
And build a wrapper class which handles Exception
i.e.
create or replace FUNCTION "FN_WRAPPER" (v_id IN VARCHAR2,v_value1 IN NUMBER,v_value2 IN NUMBER)
RETURN Varchar2
is
rs VARCHAR2(2);
tmp NUMBER;
begin
-- DBMS_OUTPUT.PUT_LINE('Hello!!!');
rs := '00';
tmp := createWith(v_id, v_value1, v_value2);
RETURN rs;
EXCEPTION
WHEN OTHERS THEN
rs := '99';
RETURN rs;
END;
This is how I avoid this error on final.
I'm going to call a function, and set some parameters by name, example:
Connection c = null;
ResultSet rs = null;
String query;
PreparedStatement ps;
CallableStatement cs = null;
try {
c = DbUtils.getConnection();
cs = c.prepareCall("{? = call get_proc_name(?, ?) }");
cs.registerOutParameter(1, OracleTypes.VARCHAR);
cs.setInt("in_proc_type", ProcTypes.SELECT);
cs.setLong("in_table_id", tableId);
// here I should use something like cs.registerOutParameter("result", OracleTypes.VARCHAR);
cs.execute();
PL/SQL function parameters are:
CREATE OR REPLACE FUNCTION get_proc_name
(
in_proc_type IN NUMBER, /*1 - insert, 2 - update, 3 - delete, 4 - select*/
in_table_name IN VARCHAR2 := NULL,
in_table_id IN NUMBER := NULL,
in_table_type_id IN NUMBER := NULL,
is_new IN NUMBER := 0
) RETURN VARCHAR2
The question is how to register result as an out parameter, and then get it from oracle to java?
I can register in/out parameters by name, because I know theirs names from function, but I don't know how go get function result, what variable name use for it.
Manuals describe only usage in/out params with procedures, not functions.
Oracle version: 11.1.0.6.0
Java version: 1.6.0_14
The solution is to use only indexes for settings parameters. Such code works as expected (mixing indexes and named parameters doesn't work; so, the problem of using named parameter for result variable could not be solved, imho):
c = DbUtils.getConnection();
cs = c.prepareCall("{? = call get_proc_name(in_proc_type => ?, in_table_id => ?) }");
cs.registerOutParameter(1, java.sql.Types.VARCHAR);
cs.setInt(2, ProcTypes.SELECT);
cs.setLong(3, tableId);
cs.execute();
String procName = cs.getString(1);
cs.close();
CallableStatement has a bunch of registerXXX methods that take index.
That's how you register the result. It is parameter number 1.
In your case,
cs.registerOutParameter( 1, java.sql.Types.VARCHAR);
<SPECULATION>
BTW, because you are using index for result, you may need to use index-oriented setXXX methods and provide a full parameter list.
</SPECULATION>
You register the function result as if it were the first parameter. Obviously, this shifts the numbering of the actual parameters.
Your already existing line
cs.registerOutParameter(1, OracleTypes.VARCHAR);
is all it takes. After the call, get your result like this:
String result = cs.getString(1);
Here's the problem (sorry for the bad english):
i'm working with JDeveloper and Oracle10g, and i have a Java Stored Procedure that is calling another JSP like the code:
int sd = 0;
try {
CallableStatement clstAddRel = conn.prepareCall(" {call FC_RJS_INCLUIR_RELACAO_PRODCAT(?,?)} ");
clstAddRel.registerOutParameter(1, Types.INTEGER);
clstAddRel.setString(1, Integer.toString(id_produto_interno));
clstAddRel.setString(2, ac[i].toString());
clstAddRel.execute();
sd = clstAddRel.getInt(1);
} catch(SQLException e) {
String sqlTeste3 = "insert into ateste values (SQ_ATESTE.nextval, ?)";
PreparedStatement pstTeste3 = conn.prepareStatement(sqlTeste3);
pstTeste3.setString(1,"erro: "+e.getMessage()+ ac[i]);
pstTeste3.execute();
pstTeste3.close();
}
I'm recording the error in a table called ATESTE because this JavaSP is a procedure and not a function, I've to manipulate DML inside.
So, the error message I'm getting is: 'parameter type conflict'...
the function "FC_RJS_INCLUIR_RELACAO_PRODCAT" it's a Java Stored Procedure too, it's already exported to Oracle, and returns an int variable, and i have to read this to decide which webservice i will call from this JavaSP.
I have already tried the OracleTyep.NUMBER in the registerOutParameter.
Anyone knows what i'm doing wrong?
It looks like you are missing a parameter in your call. You register an Integer output parameter, and then you set 2 string parameters. I'm presuming your procedure FC_RJS_INCLUIR_RELACAO_PRODCAT returns an integer value. If so your code should look more like this:
CallableStatement clstAddRel = conn.prepareCall(" { ? = call FC_RJS_INCLUIR_RELACAO_PRODCAT(?,?)} ");
clstAddRel.registerOutParameter(1, Types.INTEGER);
clstAddRel.setString(2, Integer.toString(id_produto_interno));
clstAddRel.setString(3, ac[i].toString());
clstAddRel.execute();