I was running a stored procedure with Springs SimpleJdbcCall like this:
SimpleJdbcCall jdbcCall = new SimpleJdbcCall(jdbcTemplate).withProcedureName("example_proc");
jdbcCall.execute();
// do other stuff on other subsystems
// the sysout below is just an example - the real scenario was somewhat different
System.out.println("Supposedly after the end of the stored procedure call");
The stored procedure was running for a long time, and it was overlapped with the stuff that was supposed to happen after that.
The stored procedure was written in Microsoft's SQL Server dialect, and looked like this:
CREATE PROCEDURE example_proc
AS
BEGIN
INSERT INTO example_table_1 SELECT * FROM example_table_2
UPDATE example_table_1 SET col1 = 'a' WHERE ...
END
The question is: how to make sure the SimpleJdbcCall waits until the stored procedure finishes?
There is a hack for this: make the stored procedure return something, and then retrieve it in the jdbc call.
The is the stored procedure:
CREATE PROCEDURE example_proc
AS
BEGIN
INSERT INTO example_table_1 SELECT * FROM example_table_2
UPDATE example_table_1 SET col1 = 'a' WHERE ...
-- this is just a hack for running it synchronously:
SELECT 1 AS success
END
Now that it returns something, the jdbc call can wait for that:
SimpleJdbcCall jdbcCall = new SimpleJdbcCall(jdbcTemplate)
.withProcedureName("example_proc").
returningResultSet("success", new SingleColumnRowMapper<Integer>());
Map<String, Object> map = jdbcCall.execute();
#SuppressWarnings("unchecked")
List<Integer> storedProcedureResults = (List<Integer>) map.get(success);
int result = storedProcedureResults.get(0);
// I did something to the result. I am not sure if this is really necessary.
// But I was worried if the jvm or javac would optimize the dead code.
// I returned the value from a method. Printing it should also be enough.
System.out.println(result);
Related
I am working on an application using Java, Eclipse, and Spring. We have an Oracle database that we are connecting to using JDBC.
Currently the application is using adhoc queries in the application to the database. Most of these were done by people working on the project before I came along. I have decided that using stored procedures is a better way of going. Decouple for another layer of abstraction. Not having to send the sql statement every time so less bandwidth and faster transactions. Oracle can optimize them unlike with the adhoc ones. Changes to them can occur without needing to be recompiled as long as inputs and outputs dont change. All that wonderful stuff.
The adhoc queries frequently get back multiple rows and are using the interface rowMapper and mapRow
return jdbcTemplate.query(sql, new adhoc_mapper1());
class adhoc_mapper1 implements RowMapper<List<String>> {
public List<String> mapRow(ResultSet rs, int arg1) throws SQLException{
ArrayList<String> arr1 = new ArrayList<String>();
arr1.add(rs.getString("OUT_POSITION_ID"));
arr1.add(rs.getString("OUT_POSITION_TITLE_EN"));
return arr1;
}
}
Adhoc Query in Spring
SELECT HR.POSITION_ID, HR.POSITION_TITLE_EN, HR.POSITION_TITLE_FR, HR.SECURITY_ID, HR.GROUP_NAME, HR.GROUP_LEVEL, HR.POSITION_IS_ACTIVE
FROM HR_POSITION HR JOIN DRILL_POSITION DP ON (HR.POSITION_ID = DP.POSITION_ID)
WHERE DP.TYPEVALUE = RECORD_TYPE;
Called Procedure in Oracle
CREATE OR REPLACE PROCEDURE DOCSADM.DRILL_RECORD_POSITION (
RECORD_TYPE IN VARCHAR2,
OUT_POSITION_ID OUT VARCHAR2,
OUT_POSITION_TITLE_EN OUT VARCHAR2,
OUT_POSITION_TITLE_FR OUT VARCHAR2,
OUT_SECURITY_ID OUT VARCHAR2,
OUT_GROUP_NAME OUT VARCHAR2,
OUT_GROUP_LEVEL OUT VARCHAR2,
OUT_POSITION_IS_ACTIVE OUT VARCHAR2
) AS
BEGIN
SELECT HR.POSITION_ID, HR.POSITION_TITLE_EN, HR.POSITION_TITLE_FR, HR.SECURITY_ID, HR.GROUP_NAME, HR.GROUP_LEVEL, HR.POSITION_IS_ACTIVE
INTO OUT_POSITION_ID, OUT_POSITION_TITLE_EN, OUT_POSITION_TITLE_FR, OUT_SECURITY_ID, OUT_GROUP_NAME, OUT_GROUP_LEVEL, OUT_POSITION_IS_ACTIVE
FROM HR_POSITION HR JOIN DRILL_POSITION DP ON (HR.POSITION_ID = DP.POSITION_ID) WHERE DP.TYPEVALUE = RECORD_TYPE;
END DRILL_RECORD_POSITION;
As you can see, the procedure returns multiple rows. I had asked a question about how to view the output from the procedure in Oracle but was not successful.
As the project is not using called procedures I have no examples in the code base to work back from. One of my coworkers involved in a different project has used them and was kind enough to show me their example, which regrettably was not helpful because it only called a procedure and had no returns. Their overall design also appears different. But I saw that they were using SimpleJdbcCall so I started looking online to use that.
Good examples online that I failed to get working.
I found examples that were doing what I needed to do, but not quite how I was expecting.
This example goes along the lines of what I was expecting to see and use, except that it only takes a single row as the result and I wasn't able to figure out how to alter the mapping to accept multiple rows.
This example however does use a procedure that returns multiple rows, but it uses ParameterizedBeanPropertyRowMapper and passes in a class.
This example has several ways of doing these calls but are all single row returns, but the answers comments do suggest that its easy to expand for multiple rows. But again I was unable to get it working.
I am not wanting to create a class for every procedure or family of procedures. Most of the queries end up displaying the information in a table, so I have been using a 2d object to hold and then display the data. It has been very convenient so far. I am fine with creating a mapping for each procedure as it needs to be done (unless there is a better way?), but I do not want to create a class for every procedure (and sometimes the mapper as well).
I have 150 lines of failed code attempts which I have not included to keep the question shorter. If they would help then I can include them.
TL;DR
I want to call a stored procedure in Oracle from Spring which has multiple rows being returned. I would like to use just the calling function, which either uses the RowMapper method of mapping, or an in function mapping. I want to avoid using class structures of data if possible. I am expecting/hoping it to look and get used like the first code block.
There is a basic error with my methodology for this. The way in which I was attempting to return multiple rows is wrong. As pointed out by someone in my linked question. So the ways that I was attempting to access it were also wrong. When I limited the return to a single row the following was successful.
First the imports
import java.sql.Connection;
import java.sql.CallableStatement;
Then the call
final String procedureCall = "{call DRILL_RECORD_POSITION(?, ?, ?, ?, ?, ?, ?, ?)}";
try (Connection connection = jdbcTemplate.getDataSource().getConnection();)
{
ArrayList<String> inner = new ArrayList<String>();
CallableStatement callableSt = connection.prepareCall(procedureCall);
callableSt.setString(1, "D");
callableSt.registerOutParameter(2, Types.VARCHAR);
callableSt.registerOutParameter(3, Types.VARCHAR);
callableSt.registerOutParameter(4, Types.VARCHAR);
callableSt.registerOutParameter(5, Types.VARCHAR);
callableSt.registerOutParameter(6, Types.VARCHAR);
callableSt.registerOutParameter(7, Types.VARCHAR);
callableSt.registerOutParameter(8, Types.VARCHAR);
//Call Stored Procedure
callableSt.executeUpdate();
for (int i=2; i<9; i++)
{
System.out.println(callableSt.getString(i));
}
}catch (SQLException e) {
e.printStackTrace();
}
Note that I am using the try with resources so I do not have to worry about closing the connection.
Other mistakes I made during this was not having enough ? in the String which was leading to invalid column index errors. I then made the mistake of trying to get the information before I actually executed the call.
I'm trying to use a table-valued parameter for a stored procedure we're calling using Hibernate's Session.createSQLQuery.
I have created a type and stored procedure in SQL:
CREATE TYPE StringListType AS TABLE
(
StringText NVARCHAR(256)
)
CREATE PROCEDURE [dbo].[TestStringListType]
(
#stringList StringListType READONLY
)
AS
SELECT * FROM #stringList
I can use this in SQL with:
BEGIN
Declare #StringListTemp As StringListType
insert INTO #StringListTemp (StringText)
values ('foo'), ('bar'), ('baz')
EXEC TestStringListType #StringListTemp
END
What I would like to do in Java is something like:
String fakeQueryStr = "call TestStringListType :list";
SQLQuery fakeQuery = getSession().createSQLQuery(fakeQueryStr);
ArrayList<String> data = Lists.newArrayList("foo", "bar", "baz");
fakeQuery.setParameter("list", data);
return fakeQuery.list();
Neither setParameter or setParameterList work here of course. How do I map my list of Strings to this type to use as a parameter?
I was unable to find a solution for this problem as written. My work-around was to copy the entirety of the stored procedure into a string in Java. In the above example, that would mean that I replaced:
String fakeQueryStr = "call TestStringListType :list";
with
String fakeQueryStr = "SELECT * FROM :list";
In my actual code, this was undesirable, because the stored procedure was a significantly longer set of statements, but it does still work when wrapped in BEGIN and END within the string.
I have a stored procedure that calls like this:
CREATE OR REPLACE TYPE numbers_tbl IS TABLE OF NUMBER;
declare
vess_ids numbers_tbl := numbers_tbl(91,250,251,48,339,145,172,202,174);
rez Number(19,0);
begin
dev4.limits_get_comp_catch(vess_ids, rez);
dbms_output.put_line(rez);
end;
and I'm looking for a way to call it from my java application using hibernate.
ProcedureCall call = getCurrentSession().createStoredProcedureCall("limits_get_comp_catch");
Kinda stuck on how to pass a List<Long> to the procedure. Any ideas?
DELIMITER //
CREATE PROCEDURE temp ( empId INT)
BEGIN
DECLARE var_etype VARCHAR(36);
SELECT
emptype = QOUTE(emptype)
FROM
dms_document
WHERE
id = empid;
SELECT
emptype,
CASE
WHEN emptype = 'P' THEN doctype
ELSE 'No Documents required'
END
FROM
dms_report
WHERE
pilot = 1;
End//
DELIMITER ;
I have created this procedure successfully but when I try to call it, I am getting error 1305 the function database.temp does not exist. I am trying to call using this statement:
SET #increment = '1';
select temp( #increment)
but I get Error, please tell me where I made mistake.
This is how you call it, use use the keyword call and then procedure's name
call procedureName(params);
in call of making an string
String sqlString = "procedureName("+?+")"; //in case of Integers
String sqlString = "procedureName('"+?+"')";//in case of Integers
bring the parameter in prepared statement.
MySQL's documentation on Using JDBC CallableStatements to Execute Stored Procedures explains the necessary steps quite well.
This is what your java code needs to look like:
CallableStatement cStmt = conn.prepareCall("{call temp(?)}");
cStmt.setInt(1, 42); //set your input parameter, empId, to 42.
If you want to work with the rows returned by your stored procedure's query in your Java code, you're also going to need to create an OUT parameter as noted in MySql's documentation page titled, CALL Syntax:
CALL can pass back values to its caller using parameters that are
declared as OUT or INOUT parameters
In order to call your stored procedure from MySQL workbench, use the CALL command. You can call stored procedure by directly setting values for each of the parameters:
SET #increment = 1;
CALL temp(#increment)
Then you simply use the SELECT statement to return the value of your output parameter
SELECT #outParameter
With help setting your output parameters, please read the article MySQL Stored Procedure - SELECT - Example.
Your stored procedure is syntactically wrong, and as mentioned in the comments, you're not using the stored procedure functionality for it's intended use. It's intended to be used for data manipulation not for querying. You should instead consider turning your procedure into a series of prepared statements.
Please let me know if you have any questions!
I'm connecting to SQL Server (2005) through Java using the Microsoft SQL Server JDBC Driver 2.0.
How do I get the return value from a stored procedure? I'm doing something like:
Connection connection = dataSource.getConnection()
CallableStatement proc = connection.prepareCall("{ call dbo.mySproc() }");
proc.execute();
Should I be using execute()? executeQuery()? executeUpdate()? None of these seem to return a return value by default but I'm not really sure how to get to it.
EDIT 1: To be clear, I know how to call stored procedures. This question is specifically about how to get the RETURN VALUE (as opposed to a Result Set). The Return Value is an integer that is usually generated when you execute a query with no Result Set or if you specifically state something like RETURN 0 in your SQL.
EDIT 2: executeUpdate() returns an int but this int is not the same as the Return Value. Also, an OUT parameter is not the same as a return value.
Bozho's 2nd revised answer was close but not quite there. It did lead me to the answer though.
Taking the code example I started with we end up with:
CallableStatement proc = connection.prepareCall("{ ? = call dbo.mySproc() }");
proc.registerOutParameter(1, Types.INTEGER);
proc.execute();
int returnValue = proc.getInt(1);
The key pieces here are the "? =" in front of the "call" in the prepareCall function which sets up a place for the return value and the registerOutputParameter. It has to be registered as an Integer, as the return value is always an int (at least in SQL Server, maybe it's different in other DBs). You therefore have to get it using getInt. I tested this method and it does work.
c.prepareCall("? = ..");
cs.execute();
String returnedValue = cs.getString(1);
(or the method of the appropriate type. You can use getObject alternatively)
From an old getting started tutorial
the getXXX methods in CallableStatement retrieve values from the OUT parameters and/or return value of a stored procedure.
(Btw, the links that were provided by Umesh had this sort of information.)