Operation not allowed after ResultSet closed when deleting from a database - java

I am currently trying to delete items from my database using JDBC but am getting an error that I can not figure out how to get away. The error is:
Caused by: java.sql.SQLException: Operation not allowed after ResultSet closed
Here is the java:
System.out.println("Connecting database for Delete...");
Integer deletedCount = 0;
Statement deleteStatement = null;
try (Connection conn = DriverManager.getConnection(url, username, password)) {
System.out.println("Database connected!");
deleteStatement = conn.createStatement();
String selectDelete = "SELECT id FROM table WHERE end <= (NOW() - INTERVAL 1 HOUR)";
ResultSet rs = deleteStatement.executeQuery(selectDelete);
while (rs.next()) {
String eventid = rs.getString("id");
String deleteSQL = "DELETE FROM table WHERE id = " + eventid;
deleteStatement.executeUpdate(deleteSQL);
deletedCount++;
System.out.println(deletedCount);
}
System.out.println("Completed Delete! "+ deletedCount +" deleted!");
} catch (SQLException e) {
throw new IllegalStateException("Cannot connect the database!", e);
}
What I am doing here is first selecting all the items that date has already passed and setting it to a result set. I then go through a while loop in an attempt to delete them from the database. It runs through one time and get the error which I will fully put below. Would I need to create a new delete statement every time I go through the loop? I can not figure a way to properly do this.
Here is the full error:
Connecting database for Delete...
Database connected!
1
Exception in thread "main" java.lang.IllegalStateException: Cannot connect the database!
at JDBC.deleteOverDueEvents(JDBC.java:56)
at EventJSON.main(EventJSON.java:31)
Caused by: java.sql.SQLException: Operation not allowed after ResultSet closed
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:963)
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:896)
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:885)
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:860)
at com.mysql.jdbc.ResultSetImpl.checkClosed(ResultSetImpl.java:743)
at com.mysql.jdbc.ResultSetImpl.next(ResultSetImpl.java:6313)
at JDBC.deleteOverDueEvents(JDBC.java:45)
... 1 more

Why SELECT, LOOP and DELETE when
String deleteSQL= "DELETE FROM table WHERE end <= (NOW() - INTERVAL 1 HOUR)";
Statement deleteStatement = conn.createStatement();
deleteStatement.executeUpdate(deleteSQL);
would work more efficiently

When the line
deleteStatement.executeUpdate(deleteSQL);
is executed, then ResultSet rs is automatically closed:
A ResultSet object is automatically closed when the Statement object
that generated it is closed, re-executed, or used to retrieve the next
result from a sequence of multiple results.
But you can solve this simply by running
String deleteCmd = "DELETE FROM table WHERE end <= (NOW() - INTERVAL 1 HOUR)";
int deletedCount = deleteStatement.executeUpdate(deleteCmd);
instead of selecting the records and deleting each of them.

I think this is happening because your deleting the row while looping to the result set and your logic is totally wrong. Your selecting all ids with "end <= (NOW() - INTERVAL 1 HOUR)" then delete it afterward. Why not delete it all in the first place? like this "DELETE FROM table WHERE end <= (NOW() - INTERVAL 1 HOUR)". If you need to get the count to a select statement first then to the delete query.

Related

java.sql.SQLException: No value specified for parameter 5. update database

I have this error code in the dialog box: java.sql.SQLException: No value specified for parameter 5 when i try to update my JTable/JTextFields into my SQL database.
I have checked similar questions on the site, but non seem to have the solution to my problem. I have checked the database, i have checked my connection code, the update code and can't find where this extra parameter making the problem should be? Please help a new beginner!
So now i understand that the problem is at WHERE id=? as i suspected, but my id only exist as a row count/main key in my SQL DB, so it is going to be different depending on which row you choose/click on, so i can't set a specific value beforehand at the pst.setInt(5, ? ). What to insert instead then - so i dont lose the automatic row count on my clients list in the JTable?
//This method contains all codes for database connection.
private void upDateDB() {
try {
Class.forName("com.mysql.cj.jdbc.Driver");
Connection con = DriverManager.getConnection("jdbc:mysql://localhost:3306/num klienter", "root", "");
PreparedStatement pst = con.prepareStatement("SELECT * FROM klient");
ResultSet rs =pst.executeQuery();
ResultSetMetaData StData = rs.getMetaData();
q = StData.getColumnCount();
DefaultTableModel RecordTable = (DefaultTableModel)table.getModel();
RecordTable.setRowCount(0);
while(rs.next()){
Vector<String> columnData = new Vector<String>();
for (i = 1; i <= q; i++) {
columnData.add(rs.getString("id"));
columnData.add(rs.getString("Name"));
columnData.add(rs.getString("Birthday"));
columnData.add(rs.getString("Description"));
columnData.add(rs.getString("Other"));
}
RecordTable.addRow(columnData);
}} catch (Exception ex) {
JOptionPane.showMessageDialog(null, ex);
}}
updateButton.addActionListener(new ActionListener() {
public void actionPerformed (ActionEvent arg0) {
try {
Class.forName("com.mysql.cj.jdbc.Driver");
Connection con = DriverManager.getConnection("jdbc:mysql://localhost/num klienter", "root", "");
PreparedStatement pst = con.prepareStatement("UPDATE klient SET Name=?,Birthday=?,Description=?,Other=? WHERE id=?");
table.getSelectedRow();
pst.setString(1, nameTxt.getText());
pst.setString(2, dayTxt.getText()+"-" + monthTxt.getText()+"-" + yearTxt.getText());
pst.setString(3, descriptionTxt.getText());
pst.setString(4, otherTxt.getText());
pst.executeUpdate();
JOptionPane.showMessageDialog(null, "Updated in database");
upDateDB();
}catch (Exception ex){
JOptionPane.showMessageDialog(null, ex);
}
Class.forName("com.mysql.cj.jdbc.Driver");
this can just be removed. It hasn't been neccessary for 20 years.
Connection con = DriverManager.getConnection("jdbc:mysql://localhost:3306/num klienter", "root", "");
This is a memory leak; the connection is opened, and will remain open forever; your SQL server won't allow more than a handful of open connections, so pretty soon your MySQL server will be unreachable by any service (including this java code) until you kill the java app which closes the connections. Use try with resources for all resources you create.
PreparedStatement pst = con.prepareStatement("SELECT * FROM klient");
This is also a resource and needs try-with-resources.
ResultSet rs =pst.executeQuery();
You guessed it. Try-with-resources a third time. If you find the code becoming unwieldy, JDBC is very low level and not all that great for 'end user' coding. Use a nice abstraction like JDBI or JOOQ.
columnData.add(rs.getString("Fødselsdag"));
non-ASCII in your column names? That's never going to go well. I strongly suggest you don't do it this way.
q = StData.getColumnCount();
for (i = 1; i <= q; i++) {
This is bizarre. q holds the column count - that's the number of columns in your query. And then you hardcode the 5 column names, so q is always 5. Then, you add all 5 values (id, Navn, Fødselsdag, etc), and then do that 5 times, for a total of 25 runs, and your data repeated 5 times. It is not clear what you are trying to accomplish by asking for the known information (get the column count from the metadata, which you already know).
PreparedStatement pst = con.prepareStatement("UPDATE klient SET Navn=?,Fødselsdag=?,Beskrivelse=?,Andet=? WHERE id=?");
I count 5 ?, but only 4 pst.setString statements. You forgot pst.setInt(5, theValue).
The update code gets all the same caveats about try-with-resources.
pst.setString(2, dayTxt.getText()+"-" + monthTxt.getText()+"-" + yearTxt.getText());
Not how you do date stuff with DBs. There is a pst.setDate, but optimally you should use pst.setObject, passing an instance of java.time.LocalDate. Whether MySQL actually supports that - not sure, you'd have to check.
The solution for my problem was to insert the 5th pst. statement for the id=? like this:
pst.setInt(5,table.getRowCount());

How should i make sure if the data already exists in the database

My code is having some error, and when I'm writing a roll number which is not present in the database and pressing the delete button then also a message pops up saying "record deleted successfully".
Actually I wanted to create a project of students report and by connecting java and MySQL. So I wrote code for the delete button, in which if the roll no of a student is written and pressed delete it will delete the record of that particular student.
so hope u understood my problem and looking forward for an accurate answer.
Class.forName("java.sql.DriverManager");
Connection con=DriverManager.getConnection("jdbc:mysql://localhost/stud","root","");
Statement stmt=con.createStatement();
String query="delete from info where rollno="+rn.getText();
int d = stmt.executeUpdate(query);
JOptionPane.showMessageDialog(null,"record deleted successfully!!!");
rn.setText("");
First of all, use PreparedStatement in which you fill in the parameters instead of composing a SQL-string.
May avoid very nasty errors (How does the SQL injection from the "Bobby Tables" XKCD comic work?). So
PreparedStatement stmt = con.prepareStatement("DELETE FROM info WHERE rollno=?");
stmt.setLong(1, Long.parseLong(rn.getText()));
int d = stmt.executeUpdate();
As far as your question is concerned:
The method executeUpdate returns the number of rows affected.
If it equals 0, no rows were deleted.
if (d == 0)
{
JOptionPane.showMessageDialog(null,"This record does not exist");
// Return or thrown an exception or whatever to interrupt the operation
}
else
JOptionPane.showMessageDialog(null,"record deleted successfully!!!");
showMessageDialog should get executed only if the value of d variable is positive i.e. some records got deleted from database. e.g.
Class.forName("java.sql.DriverManager");
Connection con=DriverManager.getConnection("jdbc:mysql://localhost/stud","root","");
Statement stmt=con.createStatement();
String query="delete from info where rollno="+rn.getText();
int d = stmt.executeUpdate(query);
if(d>0){
JOptionPane.showMessageDialog(null,"record deleted successfully!!!");
}
rn.setText("");
Check if result of executeUpdate is > 0. If yes, your entry was deleted.
Enter in rn: 1 or 1=1 and enjoy. Using PreparedStatements will prevent this evil SQL injection. Also it takes care of apostrophes around SQL strings and escaping apostrophe and other chars.
Connection con=DriverManager.getConnection("jdbc:mysql://localhost/stud","root","");
String query="delete from info where rollno=?";
try (PreparedStatement stmt = con.prepareStatement(query)) {
stmt.setLong(1, Integer.parseLong(rn.getText()));
int d = stmt.executeUpdate();
if (d != 0) {
JOptionPane.showMessageDialog(null, "Record deleted successfully.",
JOptionPane.INFORMATION_MESSAGE);
}
}
This try-with-resources will ensure that stmt is always closed

How to access the resultset values using multi threads

I have created below JDBC program to get the record from database
Class.forName("oracle.jdbc.driver.OracleDriver");
String url="jdbc:oracle:thin:#db-user-rw-a.qa.amazon.com:1100/MONEY";
String username = "amazon212313";
String password = "XXXXX";
System.out.println("Connecting database...");
Connection connection = null;
if (connection == null)
{
try {
connection = DriverManager.getConnection(url, username,password);
System.out.println("Database connected!");
}
catch (Exception ex)
{
System.out.println("Database Connection Failed...!!!");
System.out.println(ex);
}
}
Statement statement=connection.createStatement();
ResultSet rs1=statement.executeQuery("select account_number,flag,flag2,flag3,flag4,flag5,flag6,flag7,amount from transaction_p2");
ResultSetMetaData metadata = rs1.getMetaData();
int columnCount = metadata.getColumnCount();
System.out.println(columnCount);
while(rs1.next())
{
// account_number in if condition
if(rs1.getString(1).contains("2195281819521610731"))
{
System.out.println(rs1.getString(1));
}
}
connection.close();
I have more than 99000 records in data base and the above program is working fine but its taking huge time to retrieve the specific account number values. It's taking more than 20 minutes (Some times beyond that) to retrieve the value.
Is there any other way to speed up the search value in result set. Like creating 100 thread to search the specific account number if it found then It should return the value.
Also, there is possibly a duplicate account number in the database. All I want that I need multithreads to access the resultset and search the specific account number and return all the found account number as per if condition.
Share yours idea to achieve this task.
Updated the program with WHERE clause and observed below in console:
Updated Code:
ResultSet rs1=statement.executeQuery("select account_number,flag,flag2,flag3,flag4,flag5,flag6,flag7,amount from transaction_p2 where account_number=2195281819521610731");
ResultSetMetaData metadata = rs1.getMetaData();
int columnCount = metadata.getColumnCount();
System.out.println(columnCount);
System.out.println(rs1.next()+"\t boolean value");
while(rs1.next())
{
// account_number in if condition
System.out.println(rs1.getString(1));
}
Output:
Connecting database...
Database connected!
12
false boolean value
If I used execute() instead of executeQuery() which is returning "TRUE" but not sure how can i get resultset details.
The above account number queried in db and got result.
Why not just add the condition to your query?
instead of
select account_number,flag,flag2,flag3,flag4,flag5,flag6,flag7,amount from transaction_p2
do
select account_number,flag,flag2,flag3,flag4,flag5,flag6,flag7,amount from transaction_p2 where account_number like '%2195281819521610731%'
the last part (where account_number like '%...%') will tell SQL to only include the results whose account_number contains the given string.

Informix JBDC hold cursors over commit

Using Informix 12.10FC5DE and JDBC.4.10.JC5DE, I am trying to write a Java program that will perform a "controlled" delete on a table.
The database containing the table is logged and the table has "lock mode row".
The program will receive a maximum number of rows to delete and a perform periodic commits every X rows (to limit the number of locks and prevent long transactions).
Using SPL, I can declare a cursor with hold and do a foreach loop with "delete where current of".
Using the JDBC, I can use the resultSet methods .next() and .deleteRow() to perform what I want (I set up the connection and statement to not autocommit or close the resultSet on commit).
It works, but it is slow (under the hood the JDBC is sending something like "DELETE FROM TABLE WHERE COLUMN = ?").
The code I have is something similar to the following:
Class.forName("com.informix.jdbc.IfxDriver");
Connection conn = DriverManager.getConnection(url);
conn.setHoldability(ResultSet.HOLD_CURSORS_OVER_COMMIT);
conn.setAutoCommit(false);
String cmd = "SELECT id FROM teste_001 FOR UPDATE;";
Statement stmt = conn.createStatement(
ResultSet.TYPE_FORWARD_ONLY,
ResultSet.CONCUR_UPDATABLE,
ResultSet.HOLD_CURSORS_OVER_COMMIT);
stmt.setFetchSize(100);
stmt.setCursorName("myowncursor");
ResultSet resultados = stmt.executeQuery(cmd); // Get the resulSet and cursor
int maximo = 2000;
int passo = 100;
int cTotal = 0;
int cIter = 0;
cmd2 = "DELETE FROM teste_001 WHERE CURRENT OF " +
resultados.getCursorName();
PreparedStatement stmtDel2 = conn.prepareStatement(cmd2);
while (resultados.next())
{
if (cIter < maximo)
{
int resultCode2 = stmtDel2.executeUpdate();
if (resultCode2 == 1)
{
cTotal++;
}
cIter++;
if ((cIter % passo) == 0)
{
conn.commit(); // Perform periodic commit
}
}
else
{
break; // maximum number of rows reached
}
}
conn.commit(); // Perform final commit
stmtDel2.close();
resultados.close();
stmt.close();
conn.close();
The problem is that when I perform the 1st periodic commit, I get this error when I try the next delete:
java.sql.SQLException: There is no current row for UPDATE/DELETE cursor.
SQLCODE = -266 ; MESSAGE: There is no current row for UPDATE/DELETE cursor. ; SQLSTATE = 24000
Seems the cursor is being closed even with me setting the "HOLD_CURSORS_OVER_COMMIT".
Does anyone know if what I am trying with the JDBC is possible?
EDIT:
Since informix suports de IBM DRDA protocol i configured a drda listener on my test informix instance and used DB2 UDB JDBC Universal Driver.
The code is pretty much the same, only the driver changes:
Class.forName("com.ibm.db2.jcc.DB2Driver");
With the DRDA driver, the cursor is kept open over commits and the program behaves as expected. Tracing the session on informix instance i get this type of statement:
DELETE FROM test_001 WHERE CURRENT OF SQL_CURSH600C1
So, informix does suport "HOLD_CURSORS_OVER_COMMIT" with the DRDA driver but i still cannot make it work with the "ifxDriver".

Postgresql with java: query produced no result after insert

I write a little program to admin my video collection.
/*
insert new data set into the table
*/
int next = 0;
rs = st.executeQuery("Select max(category_id) from category;");
if (rs.next()) {
next = rs.getInt(1) + 1;
System.out.println(next);
}
String query = "INSERT INTO category VALUES (" + next + ", 'Mystics', now());";
rs = st.executeQuery(query);
//on this place is the exception thrown
// this will not execute anymore
rs = st.executeQuery("DELETE FROM category WHERE name = 'Mystics';");
The program can select on tables, make joins but insert make trouble.
I try to insert some new data in my table (see Java-code). After the second test the output show me that the data was inserted. But after Insert was an exception thrown.
1 & 2 are the tests from yesterday and today. (3) was inserted but not selected yet.
1 Mystics 2015-07-05
2 Mystics 2015-07-06
3
org.postgresql.util.PSQLException: query produced no result.
at org.postgresql.jdbc2.AbstractJdbc2Statement.executeQuery(AbstractJdbc2Statement.java:287)
at postgre_java.Zetcode.main(Zetcode.java:55)
do you have some advises for me?
Do not manipulate data with read statements!
If you want to insert, update, delete data in db use
Statement stmt = conn.createStatement();
stmt.executeUpdate(SQL);
executeQuery returns resultset, but all that INSERT, UPDATE, DELETE can return is number of affected rows and that is what executeUpdate is returning.
And never, never, never*100 use string concatenation in SQL use Prepared statements!
In Java, you use executeQuery for a SELECT statement or some other statement which returns something. If you want to execute an INSERT, UPDATE or DELETE without returning something, you should use executeUpdate().
Statement#executeUpdate() is meant for that purpose
String query = "INSERT INTO category VALUES (" + next + ", 'Mystics', now());";
int noOfRows= st.executeQuery(query)
but it doesnt return a ResultSet , rather the no of rows affected that you could store into an Integer
Also your is highly vulnerable to Sql injection , try using the PreparedStatements to safeguard your code

Categories