Terminal condition for JDBC preparedStatement.executeBatch() - java

I am using java.sql library to execute updates in batches.
When preparedStatement.executeBatch() is executed, ideally it should return an array of update counts.
But in some instances, instead of update counts it returns -2, like below..
/**
* The constant indicating that a batch statement executed successfully
* but that no count of the number of rows it affected is available.
*
* #since 1.4
*/
int SUCCESS_NO_INFO = -2;
So in this case, when I am running the queries in batches, there's no way to find whether a particular batch query execution updated any records or not. So there's no way to determine a terminal condition to exit the loop of batch execution. If anyone has a workaround or suggestion. It would be helpful.
I have the below condition right now.
private static String batchUpdate() throws SQLException {
Connection dbConnection = null;
PreparedStatement preparedStatement = null;
String result = null;
String updateSQL = "update TABLE_NAME set SOME_FLAG ='T' \n" +
" where SOME_USER!='SOMEUSER' and SOME_FLAG = 'F' and rownum<=?";
try {
dbConnection = getDBConnection();
preparedStatement = dbConnection.prepareStatement(updateSQL);
dbConnection.setAutoCommit(false);
while (true) {
preparedStatement.setInt(1, 10000);
preparedStatement.addBatch();
int[] updateResults = preparedStatement.executeBatch();
if (updateResults == null || updateResults.length == 0 || updateResults[0] == -3) {
break;
}
dbConnection.commit();
}
result = "Records are updated!";
} catch (SQLException e) {
result = e.getMessage();
dbConnection.rollback();
} finally {
if (preparedStatement != null) {
preparedStatement.close();
}
if (dbConnection != null) {
dbConnection.close();
}
}
return result;
}

Oracle 11g does not return update counts for batch methods. Instead it returns SUCCESS_NO_INFO.

I looked at some examples here, and I think you'll have to catch an exception in your code.
So this means a complete redesign of your code.
Look at the tutorial link. I think it shouldn't be that hard for you.
Also, do a rollback if you get an exception!

Related

How to make SQL delete method validate the data properly

I got a method that deletes a record in the database when inserting a tag value. when a record is deleted, a message in the console screen pops up saying "this record has been deleted ". It works fine when inserting a valid tag value. However, when I insert an invalid tag value that doesn't exist in my database it acts like it has deleted it and displays that previous message. Although within my method says if the outcome is not equal 1 (which is not true) return false, but it's apparently not validating the inserted data. Can anyone tell me what's the problem
public boolean DeleteWallet(String Tag) throws SQLException {
System.out.println("Deleting wallet");
Connection dbConnection = null;
Statement statement = null;
int result = 0;
String query = "DELETE FROM wallets WHERE Tag = '" + Tag + "';";
try {
dbConnection = getDBConnection();
statement = dbConnection.createStatement();
System.out.println("The record has been deleted successfully");
// execute SQL query
result = statement.executeUpdate(query);
} finally {
if (statement != null) {
statement.close();
}
if (dbConnection != null) {
dbConnection.close();
}
}
if (result == 1) {
return true;
} else {
return false;
}
}
The statement
System.out.println("The record has been deleted successfully");
is being printed before you actually perform any database operations statement.executeUpdate(query);
Instead, you should perform your database operation within your try statement, then print your success output. If the statement fails (IE an exception is thrown) the success statement will be skipped.
Additionally, instead of relying on the output the the executeUpdate(query) to determine if your query was successful, I would always assume your query or some operation before the query fails, and only return true if all database processing was successful.
Finally, the use of prepared statements will help make your query easier to read, use, and is better secured against SQLInjection attacks.
Example:
public class DatabaseOperations {
public boolean DeleteWallet(String Tag) {
//Query used for prepared statement
static final String DELETE_QUERY = "DELETE FROM wallets WHERE Tag=?";
System.out.println("Attempting to delete wallet using query:" + DELETE_QUERY);
//assume DELETE operation fails due to exection at any stage
Boolean result = false;
try (
//Objects that can automatically be closed at the end of the TRY block
//This is known as AutoCloseable
Connection dbConnection = getDBConnection();
PreparedStatement statment = dbConnection.preparedStatement(DELETE_QUERY))
{
//replace ? with Tag
statement.setString(1, Tag);
int row = preparedStatement.executeUpdate();
//If statement fails skip to catch block
result = true;
System.out.println("The record in row " + row + " has been deleted successfully");
} catch (SQLException sqle) {
//likely thrown due to "Record Not Found"
//TODO investigate further for the specific exception thrown from the database implementation you are using.
//TODO print helpful message to help user of this method resolve this issue
} catch (Exception) {
//TODO handle any other exceptions that may happen
}
return result;
}
}

JVM heap keeps Increasing. why?

executor = Executors.newScheduledThreadPool(1);
Runnable helloRunnable = new Runnable() {
public void run() {
controller2.GetAIntFromDatabase(columblName);
}
};
executor.scheduleAtFixedRate(helloRunnable, 0, 10, TimeUnit.MILLISECONDS);
this part of the program generates the increasing heap memory error.
controller2.GetAIntFromDatabase(columblName);
with this function I read an int value from my database.
#Override
public int GetAIntFromDatabase(String columblName) {
int stare = 0;
try{
String query = "SELECT * FROM stari ORDER BY id DESC LIMIT 1";
PreparedStatement preparedStatement = this.connnection.prepareStatement(query);
ResultSet resultSet = preparedStatement.executeQuery();
if (resultSet.next()){
stare = resultSet.getInt(columblName);
preparedStatement.close();
resultSet.close();
return stare;
}
preparedStatement.close();
resultSet.close();
}catch (SQLException ex) {
System.out.println("GetUtilajStare Error: " + ex);
return 0;
}
return 0;
}
That's the Java heap memory usage after 10 minutes of running:
Why does my heap memory keep increasing?
If an exception is thrown after opening the preparedStatement and the resultSet, they will never be closed. Therefore you should use the finally block which will always be executed.
public int GetAIntFromDatabase(String columblName) {
int stare = 0;
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
try {
String query = "SELECT * FROM stari ORDER BY id DESC LIMIT 1";
preparedStatement = this.connnection.prepareStatement(query);
resultSet = preparedStatement.executeQuery();
if (resultSet.next()) {
stare = resultSet.getInt(columblName);
return stare;
}
} catch (SQLException ex) {
System.out.println("GetUtilajStare Error: " + ex);
return 0;
} finally {
if (preparedStatement != null)
preparedStatement.close();
if (resultSet != null)
resultSet.close();
}
return 0;
}
You should use a Java 7+ way of closing the resources, try-with-resources.
The try-with-resources statement is a try statement that declares one or more resources. A resource is an object that must be closed after the program is finished with it. The try-with-resources statement ensures that each resource is closed at the end of the statement. Any object that implements AutoCloseable, which includes all objects which implement Closeable, can be used as a resource.
public int GetAIntFromDatabase(String columblName) {
final String query = "SELECT * FROM stari ORDER BY id DESC LIMIT 1";
try (final PreparedStatement preparedStatement = this.connnection.prepareStatement(query)) {
final ResultSet resultSet = preparedStatement.executeQuery();
if (resultSet.next()) {
return resultSet.getInt(columblName);
}
return 0;
} catch (SQLException ex) {
// Do something better than System.out.println(...)
return 0;
}
return 0;
}
Also you do not have to explicitly close the result set, the prepared statement does it as it owns the result set:
When a Statement object is closed, its current ResultSet object, if one exists, is also closed.
However, if you want to be paranoid and overkill it, as #MarkRotteveel suggests in his comment, you may add as an AutoCloseable resource also the ResultSet, and the code would then look like this:
public int GetAIntFromDatabase(String columblName) {
final String query = "SELECT * FROM stari ORDER BY id DESC LIMIT 1";
try (final PreparedStatement preparedStatement = this.connnection.prepareStatement(query);
final ResultSet resultSet = preparedStatement.executeQuery()
) {
...
}
I have never done it and never needed it and the documentation explicitly states that it's not necessary, but some people have experienced problems in certain edge cases.
In this second case it's especially visible how much the try-with-resources saves you from - you do not need to assign the variables to null, you do not have to check if any of them have been opened, and even if one of the close() methods throws an exception, the "closing chain" is not broken and the close() methods of the other resources are called in any case.

What's the correct way to fetch all (possibly implicit) results from an Oracle query with JDBC?

Since Oracle 12c, we can fetch implicit cursors from clients. For instance, it is possible to run the following PL/SQL anonymous block in SQL Developer
DECLARE
c1 sys_refcursor;
c2 sys_refcursor;
BEGIN
OPEN c1 FOR SELECT 1 AS a FROM dual;
dbms_sql.return_result(c1);
OPEN c2 FOR SELECT 2 AS b FROM dual;
dbms_sql.return_result(c2);
END;
To get the following result:
ResultSet #1
A
---------------------------------------
1
ResultSet #2
B
---------------------------------------
2
This works almost like a MySQL or SQL Server batch (e.g. as shown in this article), so I'm thinking that we should be able to run the following code:
try (Statement s = connection.createStatement()) {
boolean result = s.execute(sql); // Plug above SQL here
fetchLoop:
for (int i = 0;; i++) {
if (i > 0) result = s.getMoreResults();
System.out.println(result);
if (result)
try (ResultSet rs = s.getResultSet()) {
System.out.println("Fetching result " + i);
// ...
}
else if (s.getUpdateCount() == -1)
break fetchLoop;
}
}
Which results in an error with the ojdbc6 version 12.1.0.1.0:
true
java.sql.SQLException: No resultset available
at oracle.jdbc.driver.OracleStatement.getResultSet(OracleStatement.java:3369)
at oracle.jdbc.driver.OracleStatementWrapper.getResultSet(OracleStatementWrapper.java:388)
at Oracle.main(Oracle.java:46)
This seems to be in violation of the Statement.execute() method, whose Javadoc indicates:
Returns:
true if the first result is a ResultSet object; false if it is an update count or there are no results
So, once Statement.execute() yields true, Statement.getResultSet() should return a result set, in my opinion. A workaround would be:
try (Statement s = connection.createStatement()) {
s.execute(sql); // WORKAROUND: Ignore this result
fetchLoop:
for (int i = 0;; i++) {
boolean result = s.getMoreResults(); // WORKAROUND: Take the result from here
System.out.println(result);
if (result)
try (ResultSet rs = s.getResultSet()) {
System.out.println("Fetching result " + i);
// ...
}
else if (s.getUpdateCount() == -1)
break fetchLoop;
}
}
The result is now:
true
Fetching result 0
true
Fetching result 1
false
But that appears to be wrong API usage. According to my understanding of the JDBC spec, this "improved" loop would now skip the first result set.
To make matters worse, prepared statements behave differently. The following code:
try (PreparedStatement s = cn.prepareStatement(sql)) {
boolean result = s.execute();
fetchLoop:
for (int i = 0;; i++) {
if (i > 0) result = s.getMoreResults();
System.out.println(result);
if (result)
try (ResultSet rs = s.getResultSet()) {
System.out.println("Fetching result " + i);
// ...
}
else if (s.getUpdateCount() == -1)
break fetchLoop;
}
}
Doesn't fetch any result sets but simply quits:
false
It again works this way:
try (PreparedStatement s = cn.prepareStatement(sql)) {
s.execute();
fetchLoop:
for (int i = 0;; i++) {
boolean result = s.getMoreResults();
System.out.println(result);
if (result)
try (ResultSet rs = s.getResultSet()) {
System.out.println("Fetching result " + i);
// ...
}
else if (s.getUpdateCount() == -1)
break fetchLoop;
}
}
true
Fetching result 0
true
Fetching result 1
false
My questions (finally)
Assume that I'm writing generic JDBC client code, that doesn't know what the SQL string contains (it might just be an ordinary query). I want to fetch all the result sets that I may possibly get.
Does ojdbc violate the JDBC specs here?
What's the correct way to fetch all result sets from ojdbc, if the SQL string is unknown?
To be clear, the above workarounds are wrong for ordinary queries like SELECT 1 FROM dual.
For the time being (while this may or may not be a bug in ojdbc), I've found this nasty workaround to cover all usages of the JDBC API (without knowing what the SQL string produces):
/* Alternatively, use this for non-PreparedStatements:
try (Statement s = cn.createStatement()) {
Boolean result = s.execute(sql); */
try (PreparedStatement s = cn.prepareStatement(sql)) {
// Use good old three-valued boolean logic
Boolean result = s.execute();
fetchLoop:
for (int i = 0;; i++) {
// Check for more results if not already done in this iteration
if (i > 0 && result == null)
result = s.getMoreResults();
System.out.println(result);
if (result) {
result = null;
try (ResultSet rs = s.getResultSet()) {
System.out.println("Fetching result " + i);
}
catch (SQLException e) {
// Ignore ORA-17283: No resultset available
if (e.getErrorCode() == 17283)
continue fetchLoop;
else
throw e;
}
}
else if (s.getUpdateCount() == -1)
// Ignore -1 value if there is one more result!
if (result = s.getMoreResults())
continue fetchLoop;
else
break fetchLoop;
}
}

How to run multiple Delete sql statement with the help of java JDBC?

I want to delete three different tables at once and I have create a pretty simple SQL statement for that matter as following :
DELETE FROM tbl1;DELETE FROM tbl2;DELETE FROM tbl3;
this statement is correct when I run in mysql directly but from java no!
my java code :
public boolean clearTables()
{
boolean ans = false;
if (con != null)
{
try
{
String deleteQuery = " DELETE FROM tbl1;DELETE FROM tbl2;DELETE FROM tbl3;";
Statement st = con.createStatement();
st.execute(deleteQuery);
ans = true;
}
catch (Exception e)
{
e.printStackTrace();
ans= false;
}
}
return ans;
}
how can I run multiple SQL statements at once in java?
Use addBatch then executeBatch:
Statement st = con.createStatement();
st.addBatch("DELETE FROM tbl1");
st.addBatch("DELETE FROM tbl2");
st.addBatch("DELETE FROM tbl3");
int[] results = st.executeBatch();
Then results will contain an array with the number of rows deleted from each table.
use batch updates
http://docs.oracle.com/javase/1.3/docs/guide/jdbc/spec2/jdbc2.1.frame6.html

delete sql not deleting

I'm trying to delete an event from my table. However I can't seem to get it to work.
My SQL statement is:
public void deleteEvent(String eventName){
String query = "DELETE FROM `Event` WHERE `eventName` ='"+eventName+"' LIMIT 1";
db.update(query);
System.out.println (query);
}
Using MySQL db
Try using the following :
String query = "DELETE FROM `Event` WHERE `eventName` ='"+eventName+"' LIMIT 1";
try {
Connection con = getConnection();
Statement s = con.createStatement();
s.execute(query);
} catch (Exception ex) {
ex.printStackTrace();
}
You have to code your getConnection() method to return a valid Database Connection.
I would suggest using Statement.executeUpdate method, since it returns an integer. So after performing this delete query you will also have information if you really deleted any records (in this case you would expect this method to return 1, since you are using LIMIT=1). I would also suggest closing Statement as soon as you don't need it, here is skeleton implementation:
private void performDelete(Connection conn, String deleteQuery, int expectedResult) throws SQLException {
Statement stmt = conn.createStatement();
int result = -1;
try {
result = stmt.executeUpdate(deleteQuery);
if(result != expectedResult) {
//Here you can check the result. Perhaps you don't need this part
throw new IllegalStateException("Develete query did not return expected value");
}
} catch(SQLException e) {
//Good practice if you use loggers - log it here and rethrow upper.
//Or perhaps you don't need to bother in upper layer if the operation
//was successful or not - in such case, just log it and thats it.
throw e;
} finally {
//This should be always used in conjunction with ReultSets.
//It is not 100% necessary here, but it will not hurt
stmt.close();
}
}

Categories