Oracle 11g - How to call a function with DML inside of it? - java

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.

Related

java get stored procedure response

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.

Can call SQL stored procedure directly, but not in Java application

I've written a PL/SQL procedure that I'm attempting to call from a Java application. The problem is that when I call the procedure in sqlplus, it runs exactly as expected, but when it's run from the Java application, produces the following error:
SQLException Message:ORA-01453: SET TRANSACTION must be first statement of transaction
ORA-06512: at "JTRIBUNA.ASSIGN_PILOT2", line 24
ORA-06512: at line 1
The Java method is as follows:
private static boolean assignFlight() throws SQLException {
CallableStatement cstmt = null;
try {
// Prepare to call the stored procedure
System.out.println("Assigning flights...");
cstmt = connection.prepareCall("{call assign_pilot2()}");
cstmt.execute();
} catch (SQLException e) {
System.out.println("Problem with assignFlight ");
throw (e); // let caller handle after above output
} finally {
//closeCallableStatement doesn't compile
//closeCallableStatement(cstmt);
cstmt.close();
}
return true;
}
And this is the script to create the procedure:
create or replace procedure assign_pilot2 as
--cursor through flights
CURSOR fl_cur IS
SELECT * FROM flights
ORDER BY departs;
--createSP1
CURSOR pl_cur IS
SELECT e.eid, ename, a.aid, cruisingrange
FROM employees1 e
INNER JOIN certified1 c
ON e.eid=c.eid
INNER JOIN aircraft a
ON c.aid=a.aid
ORDER BY cruisingrange ASC;
pl_row pl_cur%ROWTYPE;
fl_row fl_cur%ROWTYPE;
is_assigned number;
need_delay number;
BEGIN
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
dbms_output.put_line('hi from SP assign_pilot2');
--loop through flights
FOR fl_row IN fl_cur LOOP
DBMS_OUTPUT.PUT_LINE('processing flight '|| fl_row.flno);
--loop cursor through pilots
FOR pl_row IN pl_cur LOOP
--see if the range is long enough
IF (pl_row.cruisingrange >= fl_row.distance) THEN
--see if pilot is available (not present in flight_assignments)
SELECT COUNT(*)
INTO is_assigned
FROM flight_assignments FA
WHERE FA.eid = pl_row.eid;
--if pilot is available
IF(is_assigned = 0) THEN
--add fight to flight_assignments;
INSERT INTO flight_assignments
VALUES(fl_row.flno, pl_row.aid, pl_row.eid);
EXIT;
END IF;
END IF;
END LOOP;
--if flight isn't present in flight_assignments, delay it
SELECT COUNT(*)
INTO need_delay
FROM flight_assignments FA
WHERE FA.flno = fl_row.flno;
IF(need_delay = 0) THEN
INSERT INTO delayed_flights
VALUES(fl_row.flno);
END IF;
END LOOP;
COMMIT;
END;
/
show errors;
The part that's really confusing me is that the error is a syntax one. As I mentioned before, I can call this procedure from sqlplus normally, and it does exactly what I want it to.
I'm new to working with transactional SQL, so any help will be greatly appreciated!

java code make a stored procedure return a resultset in DAO layer

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
}

ORA-03115: unsupported network data type or representation

Before issuing this question, i have googled a while, however now result found. My code attempts to call a procedure in oracle package(i am not very familiar with oracle package), and always get "ORA-03115: 不支持的网络数据类型或表示法", in english it should be 'unsupported network data type or representation'.
Below is my packagke:
create or replace
PACKAGE PKG_ACTIVITY_REPORT
IS
TYPE activity_report_item_type IS RECORD
( emp_id MERCHANT.merchant_id%TYPE,
emp_name MERCHANT.MERCHANT_NAME%TYPE,
emp_gender MERCHANT.MERCHANT_CODE%TYPE );
TYPE activity_report_items_type IS TABLE OF activity_report_item_type INDEX BY BINARY_INTEGER;
-- Procedure to retrive the activity report of given operator
PROCEDURE enquiry_activity_report(activity_report_items OUT activity_report_items_type);
END PKG_ACTIVITY_REPORT;
create or replace
PACKAGE BODY PKG_ACTIVITY_REPORT
IS
PROCEDURE enquiry_activity_report (activity_report_items OUT activity_report_items_type)
IS
activity_report_item activity_report_item_type;
BEGIN
activity_report_item.emp_id := 300000000;
activity_report_item.emp_name := 'Barbara';
activity_report_item.emp_gender := 'Female';
activity_report_items(1) := activity_report_item;
activity_report_item.emp_id := 300000008;
activity_report_item.emp_name := 'Rick';
activity_report_item.emp_gender := 'Male';
activity_report_items(2) := activity_report_item;
FOR i IN 1..activity_report_items.count LOOP
DBMS_OUTPUT.PUT_LINE('i='||i||', emp_id ='||activity_report_items(i).emp_id||', emp_name ='
||activity_report_items(i).emp_name||', emp_gender = '||activity_report_items(i).emp_gender);
END LOOP;
END enquiry_activity_report;
END PKG_ACTIVITY_REPORT;
I wanna return a array from the procedure, and call this procedure from java:
conn = ds.getConnection();
String storedProc = "{call pkg_activity_report.enquiry_activity_report(?)}";
CallableStatement cs = conn.prepareCall(storedProc);
// register output parameter
cs.registerOutParameter(1, java.sql.Types.ARRAY);
cs.execute();
Array array = cs.getArray(1);
System.out.println(array);
cs.close();
when run it, the exception thrown out. How do I map the OUT parameter to a java type? Pls help.
NOTE: when run this procedure from oracle sqldeveloper, it works properly.
DECLARE
ACTIVITY_REPORT_ITEMS RAMON.PKG_ACTIVITY_REPORT.ACTIVITY_REPORT_ITEMS_TYPE;
BEGIN
PKG_ACTIVITY_REPORT.ENQUIRY_ACTIVITY_REPORT(
ACTIVITY_REPORT_ITEMS => ACTIVITY_REPORT_ITEMS
);
END;
DBMS outputs the result:
i=1, emp_id =300000000, emp_name =Barbara, emp_gender = Female
i=2, emp_id =300000008, emp_name =Rick, emp_gender = Male
Now I created 2 schema-level type:
CREATE OR REPLACE TYPE activity_report_item_type AS OBJECT(
emp_id NUMBER,
emp_name VARCHAR2(30),
emp_gender VARCHAR2(30)
);
CREATE OR REPLACE TYPE activity_report_items_type AS TABLE OF activity_report_item_type;
And move PROCEDURE enquiry_activity_report out of package, make it independently, then call it from java successfully.
conn = ds.getConnection();
String storedProc = "{call enquiry_activity_report(?)}";
CallableStatement cs = conn.prepareCall(storedProc);
// register output parameter
cs.registerOutParameter(1, OracleTypes.ARRAY, "ACTIVITY_REPORT_ITEMS_TYPE");
cs.execute();
Array array = cs.getArray(1);
ResultSet rs = array.getResultSet();
while (rs.next()) {
// why getObject(2) instead of getObject(1)?
Object elements[] = ((STRUCT) rs.getObject(2)).getAttributes();
System.out.println(elements[0]);
System.out.println(elements[1]);
System.out.println(elements[2]);
}
cs.close();
If I put type declaration and procedure in package, java code will throw exception inform that 'no type definition found of "ACTIVITY_REPORT_ITEMS_TYPE".

statement.executeUpdate() always returns 1

I have a problem with the JDBC executeUpdate() method. It always returns 1 whether it updates a row or not. As far as I understand the method it should return 0 is no rows are altered.
Here is a sample of my code:
try {
conn = pool.getConnection();
PreparedStatement ps = conn.prepareStatement("{CALL UPDATE_USER (?,?,?)}");
ps.setString(1, field.toString());
ps.setString(2, change);
ps.setString(3, userID);
int updated = ps.executeUpdate();
System.out.println(updated);
if(updated==0){
throw new NoUserException();
}
ps.close();
} catch (SQLException e) {
log.error("An error occurred while creating the connection");
e.printStackTrace();
} finally {
pool.returnConnection(conn);
}
Could this be because I'm using a prepared statement or a stored procedure?
Here is the stored procedure:
create or replace
PROCEDURE UPDATE_USER
(
updateColumn IN user_tab_columns.column_name%type,
changeStr IN VARCHAR2,
unID IN VARCHAR2
)
IS
BEGIN
EXECUTE IMMEDIATE
'UPDATE
users
SET ' || updateColumn || '= :1
WHERE
uniqueID = :2'
USING changeStr, unID;
END;
It can't get info from stored proc execution. If you want to get row count (or anything else) switch PROCEDURE to FUNCTION, add return clause in this function, and change your call to something like ? = CALL ... Test with sql%rowcount inside function to get impacted row count.
You can use Callable statement a procedure or function. And return the no of rows affected as a return parameter

Categories

Resources