I have a procedure I'm calling using a JDBC callable statement. The first parameter, v_type, has a default value I want to use. The proc/code is simplified.
PROCEDURE select_type(v_type IN VARCHAR2 DEFAULT 'All',
cv_1 OUT SYS_REFCURSOR)
AS
BEGIN
IF (v_type = 'All')
THEN
OPEN cv_1 FOR
SELECT DISTINCT TYPE,
FROM type_source;
ELSE
IF (v_type = 'NotAll')
THEN
OPEN cv_1 FOR
SELECT DISTINCT type, desc,
FROM OTHER_TYPE_SOURCE;
END IF;
END;
This is the code I attempted, assuming null would trigger the default.
callablestatement = connection.prepareCall("{ CALL select_type(?,?) } ") ;
callablestatement.setString(1, null);
callablestatement.registerOutParameter(2, OracleTypes.CURSOR);
callablestatement.execute();
resultset = (ResultSet)callablestatement.getObject(2);
The last line .getObject() throws a cursor closed exception, presumably, due to the null parameter not matching the IF structure in the procedure and the cursor never being opened.
Related
I am calling postgreSQL function from Java using SimpleJdbcCall and extracting the output in ResultSet, while the function returns data when run in postgre but in Java it is giving null result.
Here is my postgre function that I am calling:
CREATE OR REPLACE FUNCTION package.getdemojob(create_user_id character varying)
RETURNS TABLE(batch_id bigint, content_id character varying, start_date timestamp without time zone,
end_date timestamp without time zone)
LANGUAGE plpgsql
AS $function$
BEGIN
RETURN query
SELECT MY_TABLE.BATCH_ID, MY_TABLE.CONTENT_ID, MY_TABLE.START_DATE, MY_TABLE.END_DATE
FROM MY_TABLE
WHERE MY_TABLE.CREATE_USER = create_user_id
ORDER BY BATCH_ID;
END;
$function$
;
Java code that I am using to call this function is:
this.demoGetJob = new SimpleJdbcCall(jdbcTemplate)
.withCatalogName("package")
.withProcedureName("getdemojob")
.withoutProcedureColumnMetaDataAccess()
.useInParameterNames("create_user_id")
.declareParameters(
new SqlParameter("create_user_id", Types.VARCHAR)
);
SqlParameterSource in = new MapSqlParameterSource().addValue("create_user_id", userId);
ResultSet rs = demoGetJob.executeFunction(ResultSet.class, in);
while(rs.next()){
System.out.println(rs.getInt("batch_id"));
}
On line rs.next() I am getting nullPointerException though there is data present in MY_TABLE with the given create_user_id
What wrong am I doing?
Thanks in advance.
I want to use procedure multiple times to get many table select from oracle database
My Oracle procedure
PROCEDURE getInfo(
Status IN VARCHAR2,
P_CUR OUT REFCURSOR)
AS
BEGIN
OPEN P_CUR FOR
SELECT *
FROM TABLE
WHERE TABLE.STATUS = Status
END;
Here is my Java call the the procedure. It doesn't work, I can not set registerOutParameter for PreparedStatement to get the cursor data.
PreparedStatement pstmt = null;
pstmt = cnn.prepareCall("{call " + schemaName + ".LOC_EXCHANGE.getInfo(?,?)}");
for (Entity entity : ListEntity) {
int i = 1;
pstmt.setString(i++, entity.getTxnId());
pstmt.registerOutParameter(i, OracleTypes.CURSOR);
pstmt.addBatch();
}
pstmt.executeBatch();
cnn.commit();
rs = (ResultSet) pstmt.getObject(i);
Fetching the data in bulk rather than in batch
I don't think you can batch those calls. You could probably do an anonymous block, instead:
BEGIN
LOC_EXCHANGE.getInfo(?, ?);
LOC_EXCHANGE.getInfo(?, ?);
LOC_EXCHANGE.getInfo(?, ?);
...
END;
Avoiding N+1
Personally, I think you're producing an N+1 problem here. rather than fetching data for each individual entity.getTxnId(), how about refactoring your getInfo() procedure to allow for returning data for a list of txnids? Do this:
CREATE TYPE txnids AS TABLE OF VARCHAR2(4000);
And then declare:
PROCEDURE getInfo(
Status IN txnids ,
P_CUR OUT REFCURSOR)
AS
BEGIN
OPEN P_CUR FOR
SELECT *
FROM TABLE
WHERE TABLE.STATUS IN (
SELECT * FROM TABLE (Status)
)
ORDER BY TABLE.STATUS
END;
Since you're projecting *, you'll still be able to access the STATUS column in order to group data in the client, after fetching it all.
I am in the process of converting an application from Jython to compiled Java. The application uses a host of SQL Server stored procedures to do CRUD operations. All of the procedures are defined with a return value that indicates status, and some output parameters used to provide feedback to the application. Most of the procedures also return a result set. I'm struggling with how to retrieve the return value and the result set and the output parameters.
I normally work with C# so the nuances of JDBC are new to me. I've been testing with one of the procedures that does an insert to the database and then does a select on the inserted object.
Here's a simplified example procedure just to use for the purpose of illustration. The actual procedures are more complex than this.
CREATE PROCEDURE [dbo].[sp_Thing_Add]
(
#Name NVARCHAR(50),
#Description NVARCHAR(100),
#ResultMessage NVARCHAR(200) = N'' OUTPUT
)
AS
BEGIN
SET NOCOUNT ON
DECLARE #Result INT = -1
DECLARE #ResultMessage = 'Procedure incomplete'
BEGIN TRY
INSERT INTO Things (Name, Description) VALUES (#Name, #Description)
SELECT * FROM Things WHERE ThingID = SCOPE_IDENTITY()
END TRY
BEGIN CATCH
SELECT #Result = CASE WHEN ERROR_NUMBER() <> 0 THEN ERROR_NUMBER() ELSE 1 END,
#ResultMessage = ERROR_MESSAGE()
GOTO EXIT_SUB
END CATCH
SUCCESS:
SET #Result = 0
SET #ResultMessage = N'Procedure completed successfully'
RETURN #Result
EXIT_SUB:
IF #Result <> 0
BEGIN
-- Do some error handling stuff
END
RETURN #Result
I can successfully retrieve the ResultSet using the following code.
var conn = myConnectionProvider.getConnection();
String sql = "{? = call dbo.sp_Thing_Add(?, ?, ?)}"
call = conn.prepareCall(sql);
call.registerOutParameter(1, TYPES.Integer); // Return value
call.setString("Name", thing.getName());
call.setString("Description", thing.getDescription());
call.registerOutParameter("ResultMessage", TYPES.NVARCHAR);
ResultSet rs = call.executeQuery();
// Try to get the return value. This appears to close the ResultSet and prevents data retrieval.
//int returnValue = call.getInt(1);
// Normally there'd be a check here to make sure things executed properly,
// and if necessary the output parameter(s) may also be leveraged
if (rs.next()) {
thing.setId(rs.getLong("ThingID"));
// Other stuff actually happens here too...
}
If I try retrieving the return value using the line that's commented out, I get an error stating that the ResultSet is closed.
com.microsoft.sqlserver.jdbc.SQLServerException: The result set is closed.
I've been through the documentation and have seen how to do return values, output parameters, and result sets. But how can I leverage all 3?
Given the order of processing in your stored procedure (insert, select, then populate result parameters), you need to process the result set before you retrieve the return value with CallableStatement.getXXX.
The output is in the ResultSet rs retrieved from executeQuery().
You may want to use the excute method as such:
call.execute();
String returnValue = call.getString("ResultMessage");
You also want to map correctly to the output type.
Your connection got closed once the execute query is executed. Basically mysql jdbc connection extends to AutoCloseable implicitly. Since your result is only entity from procedure,please get the value by index 0 and do a proper index out of bound exception handling.
I have my Oracle DB Stored Function as below:
CREATE OR REPLACE FUNCTION FN_EMP_CNT (EMP_ID NUMBER) RETURN NUMBER
IS
OLD_COUNT NUMBER(5) DEFAULT 0;
NEW_COUNT NUMBER(5) DEFAULT 0;
BEGIN
SELECT
COUNT(EMP_ID) INTO OLD_COUNT
FROM
OLD_DEPT
WHERE
EID = EMP_ID
AND DEPT_STAT='Closed';
SELECT
COUNT(EMP_ID) INTO NEW_COUNT
FROM
NEW_DEPT
WHERE
EID = EMP_ID
AND DEPT_STAT='Closed'
RETURN (NEW_COUNT + OLD_COUNT);
END;
When I use the below sql query directly it returns the correct number as 2:
SELECT FN_EMP_CNT(123) FROM DUAL;
But when I use Spring JDBC Template for retrieving the data it returns null.
int noOfEmps = jdbcTemplate.queryForObject("SELECT FN_EMP_CNT(?) FROM DUAL", new Object[] { empID}, Integer.class);
The most probable cause is that you use a wrong order of parameters, see Javadoc queryForObject
queryForObject(java.lang.String sql, java.lang.Class<T> requiredType, java.lang.Object... args)
Query given SQL to create a prepared statement from SQL and a list of
arguments to bind to the query, expecting a result object.
So use first the required return type followed by the parameter
This works for my fine
sql = 'SELECT FN_EMP_CNT(?) FROM DUAL'
res = jdbcTemplate.queryForObject(sql, Integer.class, 4)
HTH
I'm newbie in Java and PLSQL. I've build this procedure in PLSQL:
PROCEDURE getLogs (
p_idcontract IN NUMBER,
p_iduser IN NUMBER,
o_logs OUT VARCHAR2
)
IS
BEGIN
SELECT logData INTO o_logs
FROM SERVICELOG
WHERE IDCONTRACT= p_idcontract
AND IDUSER= p_iduser;
END getLogs;
If script detect several rows, return to the Java class who launch it this message:
ORA-01422: exact fetch returns more than requested number of rows
Please, how I can return an hashmap or similar, ready to be outputted to Java? Thanks.
I would use ref cursor as an output parameter then build map inside java program. https://oracle-base.com/articles/misc/using-ref-cursors-to-return-recordsets
You can also define a user type in oracle that will be something like Java Array.
PROCEDURE getLogs (
p_idcontract IN NUMBER,
p_iduser IN NUMBER,
p_recordset OUT SYS_REFCURSOR
)
IS
BEGIN
OPEN p_recordset FOR
SELECT logData
FROM SERVICELOG
WHERE IDCONTRACT= p_idcontract
AND IDUSER= p_iduser;
END getLogs;
Java code:
CallableStatement stmt = conn.prepareCall("BEGIN getLogs(?, ?, ?); END;");
stmt.setInt(1, 0);
stmt.setInt(2, 0);
stmt.registerOutParameter(3, OracleTypes.CURSOR);
stmt.execute();
ResultSet rs = ((OracleCallableStatement)stmt).getCursor(3);
while (rs.next()) {
;
//here build your Map, list or whatever you want
}