This question already has answers here:
Operation not allowed after ResultSet closed
(5 answers)
Closed 1 year ago.
I keep getting this whenever I try to use this method.
java.sql.SQLException: Operation not allowed after ResultSet closed
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:1055)
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:956)
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:926)
at com.mysql.jdbc.ResultSetImpl.checkClosed(ResultSetImpl.java:794)
at com.mysql.jdbc.ResultSetImpl.next(ResultSetImpl.java:7077)
at server.util.Plimus$1.run(Plimus.java:77)
This is on line 77: while (resultSet.next()) {
public static void process(final Player c, final String playerName) {
if (connection == null && connectionStatus == 0)
createConnection();
else if (connectionStatus != 0)
return;
new Thread() {
#Override
public void run() {
try {
String username = playerName.replaceAll(" ", "_");
String query = "SELECT * FROM donations WHERE username = '" + username + "' AND received = '0' LIMIT 1;";
ResultSet resultSet = query(query);
while (resultSet.next()) {
int[] contractIds = {3178768, 1}; //put all of your contract ids in here.
int contractId = Integer.parseInt(resultSet.getString("contract")), id = Integer.parseInt(resultSet.getString("id"));
query("UPDATE donations SET received = '1' WHERE username = '" + username + "' AND id = '" + id + "';");
if (contractId == contractIds[0]) { //first contract id in array.
c.getItems().addItem(962, 1);
} else if (contractId == contractIds[1]) { //second contract id in array.
c.getItems().addItem(962, 1);
}
}
} catch (Exception e) {
e.printStackTrace();
processMethod(0, true, true, false);
}
}
}.start();
}
Here is the query method that is requested.
/**
* Creates query function.
*/
public static ResultSet query(String s) throws SQLException {
try {
if (s.toLowerCase().startsWith("select")) {
ResultSet resultSet = statement.executeQuery(s);
return resultSet;
} else {
statement.executeUpdate(s);
}
return null;
} catch (Exception e) {
e.printStackTrace();
processMethod(0, true, true, false);
}
return null;
}
While iterating through your ResultSet, you are reusing the Statement object for updating the data (inside your query method), which closes the ResultSet from the query.
See the ResultSet documentation:
"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."
You should create separate Statements for the query and the update, and pass them to your query method:
public ResultSet query(String s, Statement statement) throws SQLException {
...
}
I assume that statement is declared static inside your class - there is usually no need for that: create the two statements for query and update in the constructor of your class, and either pass them to the query() method or use the one or the other depending on the statement.
Related
I created a method to get the values from a database in java using SQL and store the information in a ResultSet and then use a while loop to store the information in a RentSendItem and store all those items in an ArrayList called sendList but when I try to run it, it gives me the error:
'ResultSet not open. Operation 'getString' not permitted. Verify that autocommit is off'
This is my class:
public void getDataFromDB() {
System.out.println("Wordk");
//connecting
Connection connection = null;
Statement statement = null;
try {
System.out.println("1");
connection = DriverManager.getConnection(url, username, password);
statement = connection.createStatement();
ResultSet name = statement.executeQuery("SELECT firstname,surname FROM CUSTOMER");
ResultSet titles = statement.executeQuery("Select Title,Category From ADDDVD ");
System.out.println(name.getString("firstname"));
System.out.println("2");
while (name.next()) {
String fullName = name.getString("firstname") + " " + name.getString("surname");
RentSendItem item = new RentSendItem(name.getString("firstname") + name.getString("surname"), titles.getString("Category"), titles.getString("title"));
sendList.add(item);
}
System.out.println("3");
} catch (Exception e) {
System.out.println("Error" + e.getMessage());
}
}
So just want to know what am I doing wrong and will this class do what I want it to do. If you could maybe help me, I would be grateful.
There are several problems in your code.
You can't call method getString() of interface java.sql.ResultSet before you call method next(). First call method next() and if that method returns "true", then you can call method getString().
You also need to call method next() on titles.
You can't call getString() twice on the same column on the same row.
Compare the below with your code.
public void getDataFromDB() {
System.out.println("Wordk");
// connecting
try (Connection connection = DriverManager.getConnection(url, username, password);
Statement statement = connection.createStatement()) {
System.out.println("1");
ResultSet name = statement.executeQuery("SELECT firstname,surname FROM CUSTOMER");
ResultSet titles = statement.executeQuery("Select Title,Category From ADDDVD ");
System.out.println("2");
while (name.next()) {
String firstname = name.getString("firstname");
String surname = name.getString("surname");
String fullName = firstname + " " + surname;
if (titles.next()) {
RentSendItem item = new RentSendItem(fullName,
titles.getString("Category"),
titles.getString("title"));
sendList.add(item);
}
}
System.out.println("3");
}
catch (Exception e) {
e.printStackTrace();
}
}
Also, the following are not problems but recommendations.
In the catch block, it usually preferable to print the entire stack trace rather than just the error message.
You should close the Connection and Statement once you no longer need them. In the code above, I have used try-with-resources
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;
}
}
I am attempting to write a method that selects 2 entries into an employee database and removes them (Based on a salary field), I am currently using a counter to accomplish this, however I tried using setMaxRows() so my result set would only have two entries, thus eliminating the need for the counter. I am using try-with-resources to create my statement and that seems to be causing an issue.
public void downSize(Connection con) {
String sql = "SELECT * FROM " + schemaName + "."+tableName+" WHERE EMPLOYEE_SALARY>200000";
try (
PreparedStatement statement = con.prepareStatement(sql, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_UPDATABLE);
ResultSet rs = statement.executeQuery();
)
{
int counter = 0;
System.out.println("Now pruning workforce...");
while(rs.next() && counter<2) {
String name = rs.getString("EMPLOYEE_NAME");
rs.deleteRow();
counter++;
System.out.println(name+" was laid off.");
}
} catch(Exception e) {
e.printStackTrace();
System.out.print("Sql exception happened");
}
}
I wanted to make SQL operations easier with this class, but I am getting an exception indicating the result set is empty. The same query is working in MySQL workbench. I don't understand what the problem is.
import java.sql.*;
public class DataBase
{
private String user = "jsmith";
private String pass = "LetMeIn";
private String url = "jdbc:mysql://localhost:3306/java";
private String Schema, Table;
private Connection con;
private ResultSet Result;
private PreparedStatement pQuery;
public DataBase(String Database, String Table)
{
this.Schema = Database;
this.Table = Table;
}
private boolean createConnection()
{
boolean b = false;
try {
Class.forName("com.mysql.jdbc.Driver");
con = DriverManager.getConnection(url, user, pass);
b = true;
} catch (Exception e) {
System.out.println("Connection Exception " + e.getMessage());
e.printStackTrace();
b = false;
}
return b;
}
public String getStringData(String Desireddata, String fromColumn, String condition)
{
boolean b = createConnection();
String retrivedString = "";
if (b != false && Schema != null && Table != null) {
try {
pQuery = con.prepareStatement("SELECT ? FROM " + Schema + "." + Table + " WHERE ? = ?");
pQuery.setString(1, Desireddata);
pQuery.setString(2, fromColumn);
pQuery.setString(3, condition);
Result = pQuery.executeQuery();
Result.next();
retrivedString = Result.getString(Desireddata);
} catch (Exception e) {
retrivedString = "False";
System.out.println(e.getMessage());
e.printStackTrace();
}
} else {
System.err.println("No Connection or Database not Defined ");
retrivedString = "False";
}
return retrivedString;
}
}
And the exception:
Illegal operation on empty result set.
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:1056)
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:957)
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:927)
at com.mysql.jdbc.ResultSetImpl.checkRowPos(ResultSetImpl.java:817)
at com.mysql.jdbc.ResultSetImpl.getStringInternal(ResultSetImpl.java:5514)
at com.mysql.jdbc.ResultSetImpl.getString(ResultSetImpl.java:5434)
at com.mysql.jdbc.ResultSetImpl.getString(ResultSetImpl.java:5474)
at sync.DataBase.getStringData(DataBase.java:44)
...
You have two issues:
You can't use parameter binding on object names, so doing SELECT ? FROM... is invalid.
You aren't checking the return value from ResultSet.next(). If it returns true there is data to be read, if it returns false there is no more data.
To fix the first problem, you would need to change your code from:
pQuery = con.prepareStatement("SELECT ? FROM ... WHERE ? = ?");
to:
pQuery = con.prepareStatement("SELECT " + Desireddata + " FROM ... WHERE " + fromColumn + " = ?");
But make sure that Desireddata and fromColumn are not derived directly from user input or you will be vulnerable to a SQL injection attack.
To fix the second problem, change:
Result.next();
retrivedString = Result.getString(Desireddata);
to:
while (Result.next()) { /* You COULD use 'if' instead */
retrivedString = Result.getString(Desireddata);
}
And although not a problem, stylistically you should not have instance variables that start with a capital letter. So instead of Result you should use result or resultSet or rs. Typically you would only use a capital letter as the first character if you were defining a new type (like a class or interface).
Your resultset Result is empty. So before processing, check whether Result is null or not.
I've got a following problem: I'm trying to insert data (in this case a username) into a table using the following code:
void AddNewUser(String name, Connection conn){
if(ret == null){
ret = new DB_Retriever(conn);
}
if(!ret.UserExists(name, conn)){
try{
Statement stm = conn.createStatement();
stm.executeUpdate(DB_OperationalData.insert_new_user[0][0] + name + DB_OperationalData.insert_new_user[0][1]);
stm.executeUpdate(DB_OperationalData.insert_new_user[1][0] + name + DB_OperationalData.insert_new_user[1][1]);
stm.close();
}
catch(SQLException e){
e.printStackTrace();
}
}
}
By the way: It absolutely doesn't matter what I put in the catch clause, nothing that I put there is executed. Just to make everything clear, here is the content of the DB_OperationalData.insert_new_user String array:
public static final String[][] insert_new_user = {
{"INSERT INTO User (Username, Status) VALUES ('","','IN');"},
{"INSERT INTO Statistics (Player_ID) SELECT ID FROM User WHERE Username='","';"}};
The second statement is supposed to copy the ID of the user that is inserted and put it into Player_ID field of the Statistics table (Table User's ID is an autonumbered field).
The exception I get is:
Error while processing the query: java.sql.SQLException: ResultSet closed
What is interesting, is that it works and the data is added correctly but I simply do not want any exceptions thrown.
That's the console output I get:
This is 'data' Package Testing class
Connection to the database established.
The number of tables existing in the database is: 0
All the queries have been processed successfully
Adding new users:
Error while processing the query: java.sql.SQLException: ResultSet closed
All the lines above the Exception are my own printouts, so I know what has actually happened.
[EDIT]
I have changed the code to use the PreparedStatement instead of ordinary Statement and the current try clause looks as follows:
PreparedStatement pstm = conn.prepareStatement(DB_OperationalData.insert_new_user[0]);
pstm.setString(1, name);
pstm.addBatch();
conn.setAutoCommit(false);
pstm.executeBatch();
conn.setAutoCommit(true);
pstm.close();
And the output is (still regardless of the contents of the catch clause):
This is 'data' Package Testing class
Connection to the database established.
The number of tables existing in the database is: 0
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 0
at org.sqlite.PrepStmt.batch(PrepStmt.java:173)
at org.sqlite.PrepStmt.setString(PrepStmt.java:254)
at data.DB_Writer.AddNewUser(DB_Writer.java:28)
at data.DataHandler.AddNewUser(DataHandler.java:94)
at data.Tester.main(Tester.java:18)
All the queries have been processed successfully
Adding new users:
Error while processing the query: java.sql.SQLException: ResultSet closed
[EDIT 2]
With regards to the original version, when I remove the stm.close(); there is absolutely no difference and I still get the 'ResultSet closed' Exception.
[EDIT 3]
Here is the code of the method that is calling the above:
public void AddNewUser(String username)throws IllegalUsernameException{
if(username.length()==0 || username.length()>20){
throw new IllegalUsernameException();
}
writer.AddNewUser(username, conn);
}
The connection to the database is established by this class:
class DB_Connection {
public static Connection getConnection(){
Connection conn = null;
try{
Class.forName("org.sqlite.JDBC");
}
catch(ClassNotFoundException e){
log("Error while loading the database driver: " + e);
return null;
}
try{
conn = DriverManager.getConnection("jdbc:sqlite:database.db");
}
catch(SQLException e){
log("Unable to connect to the database: " + e);
return null;
}
return conn;
}
public static void log(String msg){
System.out.println(msg);
}
}
The DB_Retriever's method that is checking for the existing username:
boolean UserExists(String name, Connection conn){
String result = "";
try{
Statement stm = conn.createStatement();
ResultSet rs = stm.executeQuery(DB_OperationalData.user_exists[0] + name + DB_OperationalData.user_exists[1]);
result = rs.getString("Username");
}
catch(SQLException e){
System.out.println("Error while processing the query: " + e);
}
if(result.equals(name)){
return true;
}
return false;
}
The only location where Error while processing the query: java.sql.SQLException: ResultSet closed could be printed to the console is in UserExists(..), unless there is another method with a similar catch block. Indeed the ResultSet is not used correctly in UserExists, what may cause the error.
For a more complete description of how to work with JDBC look at this answer or the JDBC documentation. A possible alternative to the existing UserExists is:
boolean userExists(String name, Connection conn) {
PreparedStatement stmt = null;
try{
stmt = conn.prepareStatement("SELECT COUNT(Username) FROM User WHERE Username = ?");
stmt.setString(1, name);
ResultSet rs = stmt.executeQuery();
rs.next(); // set cursor to first row
int count = rs.getInt(1);
rs.close();
return count > 0;
} catch(SQLException e) {
// propagate error
throw new RuntimeException(e);
} finally {
// clean up resources
if (stmt != null) {
try {
stmt.close();
} catch (SQLException ignore) {
log("error on sql clean up", ignore);
}
}
}
}