I have an PL/SQL block like this:
BEGIN
FOR i IN 1..100
LOOP
UPDATE rptbody
SET comments = 'abcs';
WHERE (rptno> 100 and rptno < 200) and ROWNUM<2;
COMMIT;
END LOOP;
END;
This block needs to be executed using Oracle JDBC. I have tried the following methods:
Tried to execute this using Statement object. Since this is a block, an exception was raised saying that this is not an sql statement
This can be split up into sql statements, but I have 100s of such blocks which would be cumbersome for the code and thought of leaving this to the sqlplus.
Tried with CallableStatement which did not work as well.
Any solutions would be helpful.
This has nothing to do with how you run it. The PL/SQL syntax is invalid. You have a ; after the update clause right before the WHERE clause:
BEGIN
FOR i IN 1..100
LOOP
UPDATE rptbody
SET comments = 'abcs' --<<< no semicolon here!!
WHERE (rptno> 100 and rptno < 200) and ROWNUM<2;
COMMIT;
END LOOP;
END;
The above code can be run like this:
String sql = "... the PL/SQL block ...";
Statement stmt = connection.createStatement();
stmt.execute(sql);
Check out some code samples to use CallableStatement and PreparedStatement on Github
Tried to execute this using Statement object. Since this is a block, an exception was raised saying that this is not an sql statement
Since you were trying to execute a plsql block, you should not use Statement object.
From https://docs.oracle.com/javase/7/docs/api/java/sql/Statement.html:
The object used for executing a static SQL statement and returning the results it produces.
This is the way you need to execute a block:
CallableStatement anonBlock = null // Note that CallableStatement is an interface
String anonBlockString = '' //Generally multi line
// Set in and out parameters as needed by your anonBlockString
anonBlock.registerOutParameter( <number> , <type> )
...
// executeUpdate() is inherited from PreparedStatement and can be used for executing DML statements (update, insert, delete)
anonBlock.executeUpdate();
To access out parameters:
anonBlock.getString(<number which you have assigned in registerOutParameter() calls);
For complete example: (https://download.oracle.com/otn_hosted_doc/timesten/1121/quickstart/sample_code/jdbc/plsqlJDBC.java)
This can be split up into sql statements, but I have 100s of such blocks which would be cumbersome for the code and thought of leaving this to the sqlplus.
Prefer to use stored procedures instead of anonymous blocks. Since stored procedures are stored in a compiled and optimized format, they have a performance boost compared to anonymous ones
Tried with CallableStatement which did not work as well:
What was the code, error/stack?
Related
I'm using Postgres 12 and have written this procedure:
CREATE OR REPLACE PROCEDURE reduceStock(id INTEGER, soldQuantity INTEGER)
LANGUAGE plpgsql AS
$$
BEGIN
UPDATE inventory SET ProductStockAmount = ProductStockAmount - soldQuantity WHERE ProductID = id;
END;
$$;
It works perfectly if I open up psql on the command line and run call reduceStock(1,1);
However, calling it from my Java program as follows:
CallableStatement stmt = conn.prepareCall("{call reduceStock(?, ?)}");
stmt.setInt(1, productID);
stmt.setInt(2, quantity);
stmt.execute();
Gives me the following error:
What I've Tried
running call reduceStock(1,1); from the psql client - works perfectly
dropping the database and starting over to see if some old definition was cached - didn't work
different capitalisations, spacings of call
Any ideas would be appreciated
You need to remove the curly braces, which are the JDBC escape for calling a procedure. But because Postgres has it's own call command, they are not needed (and collides with the JDBC escape).
CallableStatement stmt = conn.prepareCall("call reducestock(?, ?)");
The curly braces around the procedure inocation ({call reduceStock(?, ?)}) mean that this is not native SQL, but rather JDBC syntax. You can read more about it here: Why do JDBC calls to stored procedures wrap the call in curly brackets?.
So calls like this still have to get translated to the native SQL by the JDBC driver. It happens that the Postgres driver, by default, treats such statements as function calls and translates them to SELECT reduceStock(?, ?) SQL query. This is not how stored procedures shall be called in Postgres. In Postgres a stored procedure call SQL is call reduceStock(?, ?).
One way to make it work would be, like #a_horse_with_no_name wrote in his answer, to remove the curly braces. This makes the statement a native call and because it's a valid Postgres SQL this is going to work. The downside is that it makes it less cross-platform as it will not work for DBs that don't support the call procname() syntax. For example this won't work for Oracle, so if you have to support multiple JDBC drivers, this is the less-preferable way to go.
A better fix would be to hint Postgres JDBC driver to treat this syntax like a stored procedure call rather than a function call and translate it to SQL accordingly. For this purpose the Postgres driver exposes a escapeSyntaxCallMode configuration property (check out the EscapeSyntaxCallMode enum as well):
Specifies how the driver transforms JDBC escape call syntax into underlying SQL, for invoking procedures or functions. (backend >= 11) In escapeSyntaxCallMode=select mode (the default), the driver always uses a SELECT statement (allowing function invocation only). In escapeSyntaxCallMode=callIfNoReturn mode, the driver uses a CALL statement (allowing procedure invocation) if there is no return parameter specified, otherwise the driver uses a SELECT statement. In escapeSyntaxCallMode=call mode, the driver always uses a CALL statement (allowing procedure invocation only).
As you can see all {call something()} statements are treated like function calls by default and always translated to SELECTs. Setting escapeSyntaxCallMode to call will make the driver translate them to call SQL statements instead. The callIfNoReturn option seems most reasonable for most use-cases as it will transform JDBC calls to stored procedure calls if no return parameter has been specified and as function calls otherwise.
You can find an example of using this setting in Postgres docs (Chapter 6. Calling Stored Functions and Procedures):
// set up a connection
String url = "jdbc:postgresql://localhost/test";
Properties props = new Properties();
// ... other properties ...
// Ensure EscapeSyntaxCallmode property set to support procedures if no return value
props.setProperty("escapeSyntaxCallMode", "callIfNoReturn");
Connection con = DriverManager.getConnection(url, props);
// Setup procedure to call.
Statement stmt = con.createStatement();
stmt.execute("CREATE TEMP TABLE temp_val ( some_val bigint )");
stmt.execute("CREATE OR REPLACE PROCEDURE commitproc(a INOUT bigint) AS '"
+ " BEGIN "
+ " INSERT INTO temp_val values(a); "
+ " COMMIT; "
+ " END;' LANGUAGE plpgsql");
stmt.close();
// As of v11, we must be outside a transaction for procedures with transactions to work.
con.setAutoCommit(true);
// Procedure call with transaction
CallableStatement proc = con.prepareCall("{call commitproc( ? )}");
proc.setInt(1, 100);
proc.execute(); proc.close();>
-- https://jdbc.postgresql.org/documentation/head/callproc.html#call-procedure-example
What is the difference between statement.executeQuery and statement.getResultSet(). I believe both will return ResultSet for a select statement but are there any specific criteria when we should use which of them.
In general you should use executeQuery if you know you are executing a select statement. The getResultSet() method by itself does not execute the statement.
The getResultSet is intended to be used in combination with execute. The execute methods are intended for use with unknown statement types, or statements that can produce multiple results (that is 0 or more update counts or result sets).
So in short: you should normally use executeQuery.
A simple example when you should use execute if the code doesn't know what query it is going to execute (an update, a query, or something more complex), for example when executing user provided queries.
Another example are SQL Server stored procedures, which can return multiple update counts and result sets.
A generic way of processing a result of execute is:
boolean isResultSet = statement.execute(sql);
while (true) {
if (isResultSet) {
try (ResultSet rs = statement.getResultSet()) {
// do something with result set
}
} else {
int updateCount = statement.getUpdateCount();
if (updateCount == -1) {
// -1 signals no more results
break;
}
// do something with update count
}
// move to next result
isResultSet = statement.getMoreResults();
}
This ensures that all* results get processed.
*: This example ignores exception results for systems (like SQL Server) that allow multiple exceptions to be interleaved with result sets and update counts, see How to get *everything* back from a stored procedure using JDBC for a more thorough example
Check out the JavaDoc for these methods. getResultSet can return null but executeQuery never return null.
There are also more constraints. For example executeQuery cannot be called on a PreparedStatement or CallableStatement.
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.
I'm having issues with stored procedures that have a lot of insert statements when using Sybase's jdbc driver. After 50-60 inserts, the stored procedure stops execution and returns. See code below.
I'm using Sybase Anywhere 10 and their jconn2.jar, but have also tried jconn3.jar.
java code:
String sp = "sp_test";
Statement stmt = con.createStatement();
stmt.execute(sp);
stmt.close();
stored procedure:
create procedure dba.sp_test()
as
begin
declare #lnCount integer
select #lnCount = 1
while (#lnCount <= 1000 )
begin
insert into tableTest (pk) values (#lnCount)
select #lnCount = #lnCount + 1
end
end
After 58 inserts the procedure returns. Doing a select count(*) from tableTest afterward returns a count of 58. There are not SQLExceptions being thrown. I tried putting a begin/commit transaction around the insert, but it didn't make a difference. I've also tried the jodbc driver and it works fine, but I'm not able to use this as a solution because I've had other issues with it.
Using executeUpdate solved the problem:
String sp = "sp_test";
Statement stmt = con.createStatement();
stmt.executeUpdate(sp);
stmt.close();
I believe if you insert con.commit() immediately after stmt.execute() would work too.
I have a prepared statement call in java that calls a procedure that commits or rolls back a sybase transaction. Is there anyway I can check from my java calls the result of that transaction, ie. success for commit or failure for rollback?
Alternatively, I want to check that the procedure completed without errors - whats the best way of doing that?
Thanks
If the rollback or commit happens within the stored procedure, then the only way to know the success is to have the stored procedure return its success status. Then, Java will have access to this result.
A stored procedure should return some kind of status or error code.
Put that in an OUT parameter and read that from the proc.
//Calling a stored procedure which takes in 2 parameters for addition
/*
--EXECUTE ADDITION 10,25,NULL
ALTER PROCEDURE ADDITION
#A INT
, #B INT
, #C INT OUT
AS
SELECT #C = #A + #B
*/
CallableStatement cs2 = con.prepareCall("{call ADDITION(?,?,?)}");
cs2.registerOutParameter(3,java.sql.Types.INTEGER);
cs2.setInt(1,10);
cs2.setInt(2,25);
cs2.execute();
int res = cs2.getInt(3);
System.out.println(res);
I think you're using the wrong approach. The client (the JAVA code in this case) should be committing or rolling back, not the sybase stored procedure. I assume here that by "calling a procedure" you mean a stored procedure that commits or rolls back.
If you use the approach I just mentioned, then you can put everything in a try/catch block and handle the commit/rollback accordingly.
try {
PreparedStatement ps = create prepared statement;
ps.execute();
// nothing went wrong, commit
} catch (SqlException e) {
// something wen't wrong, rollback
}
If by "calling a procedure" you mean just another JAVA method, then the I don't know why you would have a separate method to do a commit or rollback, you can handle it all in the try/catch block as shown above.