Calling PL/SQL procedure with SYS_REFCURSOR as IN parameter using JDBC - java

I am trying to understand how I can call a PL/SQL procedure which takes a SYS_REFCURSOR as IN parameter.
Consider the following PL/SQL procedure:
print_cursor_contents(myCursor SYS_REFCURSOR , row_count OUT NUMBER);
At the time of binding value to the IN parameter which setXXX method do I use ?
To me a java Class with individual cursor record fields , as it members and a Array of instances of this class seems the proper way to represent a plsql CURSOR. I get a SQLException when I do this:
I used the following set method
callStmt.setObject(1, curRec);
Here is the exception I got for using the above statement:
Exception occured in the database
Exception message: Invalid column type
java.sql.SQLException: Invalid column type
at oracle.jdbc.driver.OraclePreparedStatement.setObjectCritical(OraclePreparedStatement.java:8921)
at oracle.jdbc.driver.OraclePreparedStatement.setObjectInternal(OraclePreparedStatement.java:8396)
at oracle.jdbc.driver.OraclePreparedStatement.setObjectInternal(OraclePreparedStatement.java:9176)
at oracle.jdbc.driver.OracleCallableStatement.setObject(OracleCallableStatement.java:5024)
at oracle.jdbc.driver.OraclePreparedStatementWrapper.setObject(OraclePreparedStatementWrapper.java:234)
at com.rolta.HrManager.printMaxSalAllDept(HrManager.java:1022)
at com.rolta.HrManager.main(HrManager.java:1116)
Database error code: 17004

To me a java Class with individual cursor record fields , as it members and a Array of instances of this class seems the proper way to represent a plsql CURSOR.
I disagree.
If you have a stored function or procedure that either returns a ref cursor or has a ref cursor as an OUT parameter, the ref cursor comes out of JDBC as a ResultSet. So, if it were possible to call a stored procedure with a SYS_REFCURSOR parameter, I'd suspect that a ResultSet would be what you would need to pass.
In fact, my suspicions are confirmed. If you take a look at Oracle's extension to CallableStatement, OracleCallableStatement, it inherits a setCursor(int, ResultSet) method from its superinterface OraclePreparedStatement. Therefore, you could cast the CallableStatement to OracleCallableStatement, call the setCursor method, and away you go.
Except this approach doesn't actually work.
If you try calling setCursor on an OracleCallableStatement, you will get an exception java.sql.SQLException: Unsupported feature.
You can try callingsetObject with a ResultSet, but you will only get another java.sql.SQLException: Invalid column type exception.
Here's a test class you can run to verify either case. It calls one stored procedure to get a ref cursor (and hence a ResultSet) and then tries to pass it to the other:
import java.sql.*;
import oracle.jdbc.OracleTypes;
import oracle.jdbc.OracleCallableStatement;
public class JavaRefCursorTest {
public static void main(String[] args) throws Exception {
Connection conn = DriverManager.getConnection(
"jdbc:oracle:thin:#localhost:1521:XE", "user", "password");
try (CallableStatement cstmt1 = conn.prepareCall(
"{ call java_ref_curs_test.get_ref_cursor(?)}")) {
cstmt1.registerOutParameter(1, OracleTypes.CURSOR);
cstmt1.execute();
try (ResultSet rSet = (ResultSet)cstmt1.getObject(1)) {
try (CallableStatement cstmt2 = conn.prepareCall(
"{ call java_ref_curs_test.print_refcursor(?)}")) {
// Uncomment the next line to call setCursor:
// ((OracleCallableStatement)cstmt2).setCursor(1, rSet);
// Uncomment the next line to call setObject:
// cstmt2.setObject(1, rSet);
cstmt2.execute();
}
}
}
}
}
(The two procedures in the java_ref_curs_test take a single SYS_REFCURSOR parameter: get_ref_cursor returns a ref cursor and print_refcursor takes one as a parameter but does nothing with it.)
So, which setXXX method should you use? I would say none of them. What you are asking for is not possible directly.
It may still be possible to call this procedure, but you will have to create the ref cursor in PL/SQL, not in Java, and then pass it to your procedure.
For example, I could use the following PL/SQL block to call the two procedures used in the above example:
DECLARE
l_curs SYS_REFCURSOR;
BEGIN
java_ref_curs_test.get_ref_cursor(l_curs);
java_ref_curs_test.print_refcursor(l_curs);
END;
You can fairly easily run this from JDBC: put it in a string and pass it to Statement.executeUpdate().

Related

java call oracle procedure with %rowtype parameter

I have been given an oracle procedure with the in out parameter %rowtype,like:
CREATE OR REPLACE PROCEDURE cleansing(
io_user IN OUT USER%rowtype
)
IS
BEGIN
--some pl/sql code
END cleansing;
USER is a table with more than 100 columns, I want to call the procedure by Java.
I can't change the procedure, because they are already used by other project.
I can't add procedure to database, because I don't have the permission to do it.
I google it, but can't find a good way to handle this.
what I want to do is:
1. pass the parameter.
2. get the parameter. some java demo code:
String sql = "{call cleansing(?)}";
try {
dbConnection = getDBConnection();
callableStatement = dbConnection.prepareCall(sql);
callableStatement.setXXX()//I don't know
callableStatement.registerOUTParameter(1, //I don't know the type.);
can anyone help me and give some demo code? no change to database and in out parameter mapping with java
This is possible but it's not really straightforward. You have to create something of type USER%ROWTYPE at runtime and use that to call your stored procedure. Take a look here for details.
To get output values as well, you have to do something extra, along the line of Sumit's comment. Basically, after your procedure call, you open a cursor that selects the relevant data from the USER parameter.
So you get a database statement as follows (pseudocode):
string sql =
"declare
user_param user%rowtype;
begin
-- Set necessary parameters
user_param.col0 := :p0In;
user_param.col1 := :p1In;
...
-- Call procedure.
cleansing(io_user => user_param);
-- Read necessary output values into cursor.
open :pOut for select user_param.col99 as col99
user_param.col98 as col98
...
from dual;
end;"
You call this entire statement the usual way, but you register a cursor out parameter (unfortunately, Java is a very long time ago for me so I'm not sure on the exact syntax).
callableStatement.registerOutParameter("pOut", OracleTypes.CURSOR);
...
callableStatement.execute();
...
ResultSet rs = (ResultSet) callableStatement.getObject("pOut");
// Read from result set.
EDIT: I turned this into a blogpost. Code examples are in C# but the idea is the same.

Java callableStatement giving error : Attempt to set a parameter name that does not occur in the SQL

I want to execute a Oracle Stored procedure using named parameter from Java CollableStatement. Syntactically all is good by when we execute the application we get SQL Error-
Java Code -
int method1(){
CallableStatement stmt stmt = connection.prepareCall("{call "+strSQL.toString()+"}");
sp_copy_my_tree(?,?,?)
stmt.setInt("src_cd_ekey", 2057);
stmt.setInt("trg_ef_ekey", 8222);
stmt.setInt("trg_display_order", 1]);
returnValue = stmt.executeUpdate(strSQL.toString());
return returnValue ;
}
Oracle Stored Procedure -
create or replace PROCEDURE sp_copy_my_tree (src_ab_ekey IN NUMBER DEFAULT NULL,
src_cd_ekey IN NUMBER DEFAULT NULL, trg_ef_ekey IN NUMBER DEFAULT NULL,
trg_gh_ekey IN NUMBER DEFAULT NULL, trg_display_order IN NUMBER)
IS
begin
--- Some PL/SQL code ---
END ;
When I execute the above java statement I am getting Exception -
*java.sql.SQLException: Attempt to set a parameter name that does not occur in the SQL: src_cd_ekey*
Note- I have also tried to pass all the parameters in the same order of procedure, where for other 2 parameters I have passed null. But still getting the same Exception.
Please somebody help us to resolve this issue.
This issue came because of two reasons.
1) The Number of parameters declared in Procedure is different than the number of parameter passed from Java procedure call.
This is the reason why the aptly given Answer 1, to this thread cannot be used as is. We need to pass all 5 parameters from the procedure call in the proper sequence.
2) Named Parameter in Java is different then Oracle Named Parameter.
In Oracle we can execute procedure by passing only selected parameters, as the parameters passed may not be following the proper sequence so the values can be paired with the parameter name(as key). example -
EXEC sp_copy_my_tree (src_cd_ekey=>2057, trg_ef_ekey=>8222, trg_display_order=>1);
In Java we cannot emulate this and java named parameter has different meaning. In java statement we can specify the parameter name starting with colon instead of using ? as placeholder. Later while setting the placeholder we can use these parameter names rather then index. Example -
CallableStatement stmt stmt = connection.prepareCall( "{call sp_copy_my_tree(:src_cd_ekey,:trg_ef_ekey, :trg_display_order)}");
stmt.setInt("src_cd_ekey", 2057);
stmt.setInt("trg_ef_ekey", 8222);
stmt.setInt("trg_display_order", 1]);
But this call by Oracle will be considered as execution request to procedure with first 3 parameters.
Note: I was getting SqlException with stmt.executeUpdate(), so I used stmt.execute(). It might be a problem in my implementation but the latter worked in my case.
Try this method:
CallableStatement stmt = connection.prepareCall("{call sp_copy_my_tree (?,?,?}");
stmt.setInt(1, 2057);
stmt.setInt(2, 8222);
stmt.setInt(3, 1);
returnValue = stmt.executeUpdate();
return returnValue ;
}
See documentation for details: http://docs.oracle.com/cd/E11882_01/java.112/e16548/getsta.htm#i1008346

How can I execute non-query Stored Procedure in JDBC

I have a stored procedure in a postgres database. I'm using the postgres JDBC driver to execute a stored procedure, and I do not care about the return type, and can't execute the query. It's indicating that there's a syntax error near the name of the function.
In procedures that return rows, I've been able to do this via a PreparedStatement and setting the parameters, like:
PreparedStatement prepared = connection.prepareStatement("SELECT * FROM NonQueryProcedure(?)");
prepared.setInt(1, 999);
// ....
ResulSet resultSet = prepared.executeQuery();
However, I can't seem to get this to work for an "update" stored procedure where I don't care about the return type. I've tried using connection.prepareStatement() and prepareCall(), and also tried executing it with statement.execute(), .executeUpdate(), and .executeQuery(), without success.
How can I execute a stored procedure where I don't care about the return type?
As PostgreSQL has no "real" procedures, functions are simply executed using a SELECT statement:
statement.execute("select NonQueryProcedure(?)");
Note that inside a PL/pgSQL function, you can use the perform statement to call such a function. But this is not available outside of a PL/pgSQL block.
Without the actual syntax error, I can't say for sure, but try this:
"SELECT * FROM \"getData\"(?)"
CamelCase/PascalCase is a BAD idea in any SQL database. Either it folds it to a single case and all you see is AMASSOFUNREADABLELETTERS or it requires quoting and you will have to forevermore type "aMassofLettersAndQuotesAndShiftKeysAndMyFingersHurt" anytime you want to avoid a syntax error.

How to call PostgreSQL procedure in Java?

How to make query like this in Java and get the results:
SELECT filedata.num,st_area(ST_Difference(ST_TRANSFORM(filedata.the_geom,70066),filedata_temp.the_geom))
FROM filedata, filedata_temp
Where filedata.num=filedata_temp.num
Or, I think will be better if I create procedure in Postgres from this query.
CREATE OR REPLACE FUNCTION get_geom_difference()
RETURNS void AS
$$
BEGIN
SELECT filedata.num,st_area(ST_Difference(ST_TRANSFORM(filedata.the_geom,70066),filedata_temp.the_geom))
FROM filedata, filedata_temp
Where filedata.num=filedata_temp.num
end;
$$
LANGUAGE 'plpgsql'
and call it
Connection ce_proc= null;
ce_proc = DriverManager.getConnection("jdbc:postgresql://localhost:5432/postgis","postgres","123456");
java.sql.CallableStatement proc = ce_proc.prepareCall("{get_geom_difference()}");
proc.execute();
proc.close();
ce_proc.close();
But how to get results from this procedure in Java?
UPDATE
I tried this SP
DROP FUNCTION get_geom_difference();
CREATE OR REPLACE FUNCTION get_geom_difference()
RETURNS integer AS
$$
DECLARE
tt integer;
BEGIN
SELECT filedata.num INTO tt
FROM filedata
Where filedata.num=1;
RETURN tt;
END;
$$
LANGUAGE 'plpgsql'
and call
Class.forName("org.postgresql.Driver");
Connection connect= null;
connect = DriverManager.getConnection("jdbc:postgresql://localhost:5432/postgis","postgres","123456");
java.sql.CallableStatement proc = connect.prepareCall("{?=call get_geom_difference()}");
proc.registerOutParameter(1, java.sql.Types.INTEGER);
proc.executeQuery();
ResultSet results = (ResultSet) proc.getObject(1);
and got an error:
org.apache.jasper.JasperException: An exception occurred processing
JSP page /commit_changes.jsp at line 25in lineproc.executeQuery();
root cause javax.servlet.ServletException:
org.postgresql.util.PSQLException: No results were returned by the
query
But query
SELECT filedata.num
FROM filedata
Where filedata.num=1;
returns 1.
Where is mistake?
You can largely simplify the function. (Keeping simplistic function for the sake of the question.)
CREATE OR REPLACE FUNCTION get_geom_difference()
RETURNS integer AS
$BODY$
SELECT num
FROM filedata
WHERE num = 1
LIMIT 1; -- needed if there can be more than one rows with num = 1
$BODY$ LANGUAGE SQL;
Though, technically, what you have in the question would work, too - provided the data type matches. Does it? Is the column filedata.num of type integer? That's what I gather from the example. On your other question I was assumingnumeric for lack of information. At least one of them will fail.
If the return type of the function doesn't match the returned value you get an error from the PostgreSQL function. Properly configured, your PostgreSQL log would have detailed error messages in this case.
What do you see, when you create the above function in PostgreSQL and then call:
SELECT get_geom_difference(1);
from psql. (Preferably in the same session to rule out a mixup of databases, ports, servers or users.)
Calling a simple function taking one parameter and returning one scalar value seems pretty straight forward. Chapter 6.1 of the PostgreSQL JDBC manual has a full example which seems to agree perfectly with what you have in your question (My expertise is with Postgres rather than JDBC, though).
There are quite a few different CallableStatement constructors, but only two of them let you get results back.
A ResultSet is returned by CallableStatement.executeQuery(). There's a good complete example in the link above.
I don't know if getting a scalar result back from a CallableStatement is legal. I'd expect PgJDBC to translate it to a rowset of one row, though, so it should work.
Your query example is typical. So what you will need is
Java Database Connectivity (JDBC)
and everything needed to serv it, is in package java.sql
So at this point I recoemnd you to read some tutorial and if you have some particular problem write about it on SO.
You will need JDBC to do that. You should be able to find all JDBC related information here.
Take a look here for a more detailed tutorial on how to connect your Java application to your PostgreSQL.
works 100% java 7 and postgres pgadmin 2016, Use createNativeQuery In your transaction write this
and change myschema.mymethodThatReturnASelect
for the scheme and the name of your function.
#Override
public List<ViewFormulario> listarFormulario(Long idUsuario) {
List<ViewFormulario> list =null;
try {
Query q = em.createNativeQuery("SELECT * FROM myschema.mymethodThatReturnASelect(?);");
q.setParameter(1, idUsuario);
List<Object[]> listObject = (List<Object[]>) q.getResultList();
if (listObject != null && !listObject.isEmpty()) {
list = new ArrayList<>();
for (Object o[] : listObject) {
ViewFormulario c = new ViewFormulario();
c.setIdProyecto(o[0] != null ? Long.valueOf(o[0].toString()) : -1L);
...etc...etc.

Errors when fetching sys cursor from plsql using jdbc

I have a stored procedure which returns sys_refcursor and I am trying to fetch the cursor from java using JDBC.
plsql stored procedure
create or replace procedure my_proc(p_deptno IN number,p_emp_no IN varchar2
, p_cursor OUT SYS_REFCURSOR)
is
begin
open p_cursor FOR
select *
from emp
where deptno = p_deptno and emp_number=p_emp_no;
end proc;
/
java code
callablestatement = connection.prepareCall("{cal my_proc(?,?,?)} ");
callablestatement.setInt(1, param1);
callablestatement.setString(2, param2);
callablestatement.registerOutParameter(3, OracleTypes.CURSOR);
callablestatement.execute();
resultSet = ((OracleCallableStatement)callablestatement).getCursor(4);
while (resultSet.next()) {
<classname> = mapList(resultSet);
logger.info(resultSet.getString(1));
}
When I execute the above I am getting the following execeptions
java.lang.NullPointerException at callablestatement.execute();
and
Non supported SQL92 token at position: 3: cal
You have a typo in your statement:
{cal my_proc(?,?,?)}
should be
{call my_proc(?,?,?)}
use theCallableStatement.setObject( instead
Of
.setString and .setInt etc
it has also theCallableStatement.setNull for null
but keep registerOutParameter the same
description for the method
Sets the value of the designated parameter with the given object. The
second parameter must be of type Object; therefore, the java.lang
equivalent objects should be used for built-in types. The JDBC
specification specifies a standard mapping from Java Object types to
SQL types. The given argument will be converted to the corresponding
SQL type before being sent to the database. Note that this method may
be used to pass datatabase- specific abstract data types, by using a
driver-specific Java type. If the object is of a class implementing
the interface SQLData, the JDBC driver should call the method
SQLData.writeSQL to write it to the SQL data stream. If, on the other
hand, the object is of a class implementing Ref, Blob, Clob, NClob,
Struct, java.net.URL, or Array, the driver should pass it to the
database as a value of the corresponding SQL type. This method throws
an exception if there is an ambiguity, for example, if the object is
of a class implementing more than one of the interfaces named above.
Note: Not all databases allow for a non-typed Null to be sent to the
backend. For maximum portability, the setNull or the setObject(String
parameterName, Object x, int sqlType) method should be used instead of
setObject(String parameterName, Object x).
I have resolved the issue by using a function with the same code as in the procedure and it has resolved my issue.
And I called my function using the following manner.
private final String FUNCTIONAME= "begin ? :=myfunc(?,?,?); end;"";
Thanks

Categories