Callable or PreparedStatement and transactions - java

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.

Related

Log how many items have been impacted in a Spring/Hibernate transaction

I have this method that is called from another service:
#Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
public void execute(String sql) {
Query query = entityManager.createNativeQuery(sql);
query.executeUpdate();
}
Basically the client loads multiple sql files and run each sql file in a new transaction, in order to not impact other files execution.
For example this is an example of an sql file, that is cleaning up some data:
begin;
delete from table t where t.created_at < current_date - interval '2 month';
commit;
What I'm trying to do is to log, the outcome of each transaction. For example here, I want to display how many records were deleted. How can I do that from Spring ? I know that you can log something more specific with:
logging.level.org.springframework.transaction=TRACE
, but still I cannot see any outcome. This reveals information about sql that will run and when transaction started/ended.
Second solution was to check the result of:
int count = query.executeUpdate();
, but count is 0, even though the sql code got executed and deletes hundreds of rows.
Thanks upfront for the suggestions !
The problem is as #XtremeBaumer correctly pointed out your script. If you just run executeUpdate with a delete statement it will return the number of affected rows.
But that is not what you are doing. You are executing a code block delimited by begin and end. There might be a way for such a code block to return a value, but that would need to be coded into the code block and is probably highly database specific.

Stored procedures doesn't execute when stopping Java

I created a stored procedure in MySQL and called it from a Java application with JPA EclipseLink. Once the procedure is called, it has a "sleep(sec)" method inside, and then it executes something successfully unless the application is turned off, it seems like the procedure is canceled too which is not what I want. The same thing I tried using JDBC PreparedStatements, the same result. Is there any workaround to make the stored procedure work even if the app was shut down after the procedure call.
Stored Procedure
DELIMITER //
DROP PROCEDURE IF EXISTS session_procedure//
CREATE PROCEDURE session_procedure
(
IN userID INT
)
BEGIN
SELECT SLEEP(30);
UPDATE users
SET users.Active = 0
WHERE users.Id = userID;
END//
DELIMITER ;
Procedure call in Java
public static void destroySessionCountdown(EntityManager entityManager, Account akk){
int accountId = akk.getId();
StoredProcedureQuery storedProcedure = entityManager.createStoredProcedureQuery("session_procedure");
storedProcedure.registerStoredProcedureParameter(1, Integer.class, ParameterMode.IN);
storedProcedure.setParameter(1, accountId);
try {
storedProcedure.execute();
}catch(Exception e){
e.printStackTrace();
}
// force logout
}
I suppose that when you closed connection to the DB, all processes related to it were cancelled including the running call of this stored procedure. I don't think you can avoid it.
What you are trying to implement is a kind of scheduled job. I would suggest to use cron instead. For the procedure you shown a simple SQL instead of stored procedure would be sufficient. The logic related to delays and to the execution time could be placed to a shell script and to the cron.

ExecuteQuery() vs getResultSet() in Java

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.

How to execute an Oracle PLSQL block in Java

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?

Is there a bug with "set chained" in setAutoCommit() in net.sourceforge.jtds.jdbc.Driver?

I am having some confusion with set chained statement in setAutoCommit() method in net.sourceforge.jtds.jdbc.Driver
The source code says:
2161 if (serverType == Driver.SYBASE) {
2162 if (autoCommit) {
2163 sql.append("SET CHAINED OFF");
2164 } else {
2165 sql.append("SET CHAINED ON");
2166 }
However, shouldn't it be backwards, and chaining should be OFF for autoCommit==false?
The reason I ran into this is as follows:
I am writing a Java app which needs to do some complicated SQL and roll back all of it if any of it fails:
Open Sybase connection using net.sourceforge.jtds.jdbc.Driver
Call setAutoCommit(false)
Do SQL1
Call stored proc 'MySP1'
Stored proc MySP1' is NOT under my control
It has EXEC sp_procxmode 'dbo.MySP1','unchained'
Do SQL2
If SQL2 fails, roll back everything (including SQL1), otherwise commit.
Having done that, I get the following error from MySP1:
Stored procedure 'MySP1' may be run only in unchained transaction mode. The 'SET CHAINED OFF' command will cause the current session to use unchained transaction mode.
I had almost the same problem and solved it by running following SQL. hopefully it can help you.
sp_procxmode your_stored_Procedure, 'anymode'
in your case your_stored_Procedure = MySP1 then you should run following code:
sp_procxmode MySP1, 'anymode'
Just in case somebody looks here after long 7 years,
I've made it work by
connection.setAutoCommit(false); // this is mandatory, as without it jTDS will commit each statement.
in java, and explicitly executing SQL code:
SET CHAINED OFF
begin transaction

Categories