SQL queries through Java, "Illegal operation on empty result set" - java

I'm making a db call as follows:
String sqlAlert = "SELECT * FROM demotable where demo_no ='"
+rsDemo.getString("demo_no") + "'";
ResultSet rsAlert = db.GetSQL(sqlAlert);
if (rsAlert.next()) {
String newAlert = rsAlert.getString("cust3")+"1";
String newAlertSql = "UPDATE demotable SET cust3 = '" + newAlert + "' where demo_no='" + rsDemo.getString("demo_no") + "'";
System.out.println("Update alert msg: " + newAlertSql);
db.RunSQL(newAlertSql);
} else {
System.out.println("empty result. Demo_no = "+rsDemo.getString("demo_no"));
String sqlAlertinsert = "INSERT INTO demotable VALUES('" + rsDemo.getString("demo_no") + "','','','','','<unotes></unotes>')";
db.RunSQL(sqlAlertinsert);
System.out.println("insert demo done");
String sqlAlert2 = "SELECT * FROM demotable where demo_no ='"rsDemo.getString("demo_no") + "'";
ResultSet rsAlert2 = db.GetSQL(sqlAlert2);
if (rsAlert2.next()) {
String newAlert = rsAlert2.getString("cust3")+"1";
String newAlertSql = "UPDATE demotable SET cust3 = '" + newAlert+ "' where demo_no='" + rsDemo.getString("demo_no") + "'";
System.out.println("Update alert msg: " + newAlertSql);
db.RunSQL(newAlertSql);
}
rsAlert2.close();
}
rsAlert.close();
rs.close();
I am trying to insert rows into demographiccust if rsAlert returns an empty set and then access values from it. But my code returns this exception "Illegal operation on empty result set" around "if (rsAlert2.next()) { ". Why does it return an empty set even after inserting values into the table? Please help. Thank you.

It may be because of the open cursor. You must close your first Statement, prior trying the second. ResultSet is a connected thing, when you close the Statement it get closed too. I can't see the implementation of your db.RunSQL() and db.GetSQL() methods.
However, I am having the suggestion on how you should do it, in the first place. Here you go,
Update it without querying the database
Check how many rows updated. If none, then step 3, otherwise completed
Insert the record with the correct values in the first place. No need to update it after inserting.
Tips:
Try using PreparedStatement, instead
Try to stick with Java Naming Convention
Try using meaningful names, i.e. for example your method db.GetSQL() is not returning an SQL, but contrarily asking one, and in fact returning a ResultSet.
Never return a ResultSet. This may lead to bloated code and a lot of open cursors. Don't make the user of your method to close it. Close it yourself in your method where you are performing any database query, and return the result as a bean or a list of beans.

It's just a guess, but because you are interpolating rsDemo.getString("demo_no") directly into the SQL, you may be passing an SQL statement that isn't what you want. Try using the parameter binding api.

Related

Error when updating MySQL database using UPDATE - SET - WHERE method in Eclipse

I am making a program using Eclipse that allows the user to update the volume of chemicals everytime they’re restocked/used, which requires them to enter the ID of the chemical and the amount they would like to add/subtract. A query is then performed to search for the chemical's ID in the database, and its volume is updated accordingly.
However, I’m having difficulties getting the volume to update. I tried adapting MySQL’s UPDATE statement from this website to SET volume = volume + amount added, WHERE chemical ID = ID entered by the user; however, there appears to be some syntax errors in my code, more specifically at the UPDATE - SET - WHERE line:
public void IDEnter() {
try {
Class.forName("com.mysql.cj.jdbc.Driver");
Connection con = DriverManager.getConnection("jdbc:mysql://localhost:8889/StockControlSystem","root","root");
Statement stmt = con.createStatement();
String sql = "Select * from Chemicals where `Chemical ID` ='" + txtChemical_ID.getText()+"'";
ResultSet rs = stmt.executeQuery(sql);
if(rs.next()) {
stmt.executeUpdate("UPDATE Chemicals" + "SET `Volume` = rs.getInt(Volume) + Integer.parseInt(AmountAdded.getText()) WHERE `Chemical ID` in (txtChemical_ID.getText())");
}
else {
JOptionPane.showMessageDialog(null, "Invalid chemical ID");
txtChemical_ID.setText(null);
}
} catch(Exception exc) {
exc.printStackTrace();
}
}
Since I'm still new to MySQL, can someone help me correct this? Thank you so much for your help!
Your whole query is badly formatted. Change your code to this:
stmt.executeUpdate("UPDATE Chemicals SET Volume = " +
rs.getInt(Volume) + Integer.parseInt(AmountAdded.getText())
+ " WHERE Chemical_ID in (" + txtChemical_ID.getText() + ")");
You cannot use ' single quotes when defining Column names in queries. Single quotes are used for string values!
Still, this would not be the best way to do this. use PreparedStatement!
This way:
String updateString = "UPDATE Chemicals SET Volume = ? WHERE Chemical_ID in (?)"; // Creation of the prepared statement, the ? are used as placeholders for the values
PreparedStatement preparedStatement = con.prepareStatement(updateString);
preparedStatement.setInt(1, rs.getInt(Volume) + Integer.parseInt(AmountAdded.getText())); // Setting the first value
preparedStatement.setString(2, txtChemical_ID.getText()); // Setting the second. I am supposing that this txtChemical_ID textField has values seperated by commas, else this will not work!
preparedStatement.executeUpdate();
If you need to read more for PreparedStatement there are a lot of great resources out there. They also protect against SQL injections.
I think your problem might be with the "rs.getInt(Volume)"
Yours:
"UPDATE Chemicals" + "SET `Volume` = rs.getInt(Volume)
+ Integer.parseInt(AmountAdded.getText())
WHERE `Chemical ID` in (txtChemical_ID.getText())"
Can you try this:
"UPDATE Chemicals" + "SET `Volume` = " +
Integer.parseInt(AmountAdded.getText()) + "
WHERE `Chemical ID` in (" + (txtChemical_ID.getText()) +")"

ResultSet is from UPDATE. No Data

The problem occurs when executeQuery function runs, the sql statement is work correctly and gives correct results when it is runned on the sql editor. When it is runned on jdbc it is not executed. The connection accepts multi queries.
String query = "set #countOfLectureGrade = (SELECT Count(goc.Affect) FROM GradeOfCourse goc WHERE goc.LectureID = ?);"
+ "SELECT u.SchoolID, u.Name, u.Surname, u.Role, u.Email, "
+ "CASE WHEN #countOfLecture = 0 then 0 "
+ "ELSE AVG(0.01 * goc.Affect * gos.Grade) "
+ "END AS Average "
+ "FROM GradeOfCourse goc, GradeOfStudent gos, User u, CourseOfStudent cos "
+ "WHERE "
+ "(gos.CourseGradeID = goc.GradeID AND u.SchoolID = gos.StudentID AND goc.LectureID = ?) "
+ "OR (u.SchoolID = cos.SchoolID AND cos.LectureID = ? AND #countOfLectureGrade = 0) "
+ "GROUP BY u.SchoolID;";
try {
connection = super.getConnection();
PreparedStatement sqlStatement = connection.prepareStatement(query);
sqlStatement.setInt(1, lectureID);
sqlStatement.setInt(2, lectureID);
sqlStatement.setInt(3, lectureID);
ResultSet resultSet = sqlStatement.executeQuery();
java.sql.SQLException: ResultSet is from UPDATE. No Data.
This is not possible, you have to separate your queries, of for the best solution you can use procedures or function.
Procedure should take lectureID
Return your result, in your case it should multiples valus, you can read How to retrieve multiple rows from stored procedure in mysql? to know how to use procedure return multiple values
I am not familiar with JDBC but a quick search suggests you should use execute rather than executeQuery.
execute: Returns true if the first object that the query returns is a
ResultSet object. Use this method if the query could return one or
more ResultSet objects. Retrieve the ResultSet objects returned from
the query by repeatedly calling Statement.getResultSet.
https://docs.oracle.com/javase/tutorial/jdbc/basics/processingsqlstatements.html#executing_queries
Have a look at the following documentation which explains to use execute() instead of executeQuery() and then fire a getResultSet() on the Result set that you get.
The whole approach is to change your query into a Stored procedure and invoke the same through a CallableStatement.
Documentation suggests:
Although CallableStatement supports calling any of the Statement execute methods (executeUpdate(), executeQuery() or execute()), the most flexible method to call is execute(), as you do not need to know ahead of time if the stored procedure returns result sets.
Hope this helps!

How do I check for value existence / why is ResultSet closed in SQLite?

Setup
I am using JDBC SQLite for my server database.
What I am trying to do
I want to check if a value (for example an ID or a username) already exists in a table.
My Code
PreparedStatement preparedStatement = connection.prepareStatement("select * from " + tableName + " where " + columnName + "=? limit 1");
preparedStatement.setString(1, value);
ResultSet resultSet = preparedStatement.executeQuery();
resultSet.next();
try { // this was just for testing and is dumb because it prints an error every time the value does not exist.
// my code actually wasn't wrong. I just tested it badly ..
// it's replacable with if (resultSet.next())
resultSet.getString(1);
System.out.println("check true");
return true;
} catch (Exception e) {
e.printStackTrace();
System.out.println("check false");
return false;
}
Here I want to check if a specific value already exists in my column(Name) in my table.
The Problems
This code will always throw a SQLException: "ResultSet closed" at resultSet.getString(columnName). It does not matter if the value already exists in the table or not. My query could also be wrong because I don't get what I should put after select. I had a 1 there once, but everyone was using * for "all" and it changed nothing. I seriously don't get this because it is too complicated for me and I just wanted to know what the argument after select does..
Approaches
I tried many things and read a lot of threads, but the only thing I came across was that I need to first call resultSet.next() and that the ResultSet won't throw ResultSet closed exception that way, but it didn't change anything.
If there is a better way to check if a value already exists in a table, please tell me.
Visualisation
I have a table here (the NULL's don't matter they could be some random values) and I want to check ggffvgvg exists or not.
Complete StackTrace
java.sql.SQLException: ResultSet closed
at org.sqlite.core.CoreResultSet.checkOpen(CoreResultSet.java:69)
at org.sqlite.core.CoreResultSet.markCol(CoreResultSet.java:96)
at org.sqlite.jdbc3.JDBC3ResultSet.getString(JDBC3ResultSet.java:436)
at Database.check(Database.java:50)
at ClientHandler.run(ClientHandler.java:72)
resultSet.next() returns a boolean which tells you whether there is a next record or not. Ignoring this returned value is like shooting your own foot with a rocket launcher.
So, if the value you are looking for is not in the result set, (either because it really isn't, or because you did not ask for it properly,) resultSet.next() will return false, and your code will blow up with the exception that you are receiving.
Your resultSet.getString(1) clause will fetch the first column in the result set, (if there is a current row,) and it will return it to you assuming that it is in fact a string. The first column of most tables is usually an integer, or some other kind of key data type, which means that it is rarely a string. If you are lucky, it will be something that the JDBC driver can convert to a string, but you are tempting your fate by assuming that.
If you are only going to check the value of a single column, then your query statement must select that column only. This means that instead of
"select * from " + tableName + " where " + columnName + "=? limit 1"
you must do
"select " + columnName + " from " + tableName + " where " + columnName + "=? limit 1"
However, if I understand correctly what you are trying to achieve, you do not even need to fetch the field and check its value. Simply the true or false result of resultSet.next() should suffice.

JDBC exception "Operation not allowed after ResultSet closed" when iterating over ResultSet

I have a problem with this code:
ResultSet dane = statement.executeQuery("SELECT * FROM ProWarns WHERE blahblah = '" + cel + "'");
while (dane.next()) {
// some code (only get some string from result)
if (TimeEnd <= EndTime) {
statement.executeUpdate(
"DELETE FROM ProWarns WHERE id = '" + id + "'"); //and error
statement.executeUpdate(
"UPDATE ProWarnsPlayers SET num = '" + NewV + "'" WHERE Pl = '"+ tar + "'");
}
}
Error: java.sql.SQLException: Operation not allowed after ResultSet closed. Where is the bug and how can I fix it?
PS:
I am including a Pastebin of my DB class, in case its helpful.
A Statement object caches its result set, so when you execute the additional operations in your for loop the original result set gets 'reset'. Which leads to the error that is happening, when you call dane.next. From the Javadoc:
By default, only one ResultSet object per Statement object can be open
at the same time. Therefore, if the reading of one ResultSet object is
interleaved with the reading of another, each must have been generated
by different Statement objects. All execution methods in the Statement
interface implicitly close a statment's current ResultSet object if an
open one exists.
Options? Use another Statement object to execute the inner queries.

Prepared statement fails, but SQL console works

I am working on a project for uni (happens to be due in 14 hours) and I am at a sticking point. It is a web based web store running in eclipse on apache tomcat and derby.
I have a prepared statement that checks for a user name and passwordhash, no matter what I try this statement returns 0 rows. The same sql runs in the sql scratch pad and returns what is expected.
I have used the debugger to inspect the prepared statement object and the query seems fine. The ?'s in the text are still in place rather than filled with the variables, but that seems normal. I have also tried to run the exact same hand written sql from the console, but without any luck.
The query I run in the sql console is
SELECT * FROM username WHERE username='user#system.com' AND passwordhash='passwordhash'
The prepared statments look like this.
PreparedStatement pstmt = db.prepareStatement("SELECT * FROM reallynice.username " +
"WHERE emailaddress=?" +
" AND passwordhash=?");
pstmt.setString(1,username);
pstmt.setString(2, username + ":" + passwordLogin);
I am at the point where I have tried everything, and have run out of searches to make. I know this is a uni project and the standard reply is to give people somewhere to look. At this point I need spoon feed a path to go down.
EDIT Here is some more background, I have tried running a known working query in this pipeline and it also fails to return any rows.
public static User getUser(String username, String passwordHash) {
DBBean db = new DBBean();
System.out.println("Logging in for username " + username + " and password " + passwordHash);
try {
ResultSet rs;
PreparedStatement pstmt = db.prepareStatement("SELECT * FROM reallynice.username " +
"WHERE emailaddress=?" +
" AND passwordhash=?");
pstmt.setString(1,username);
pstmt.setString(2,passwordHash);
//PreparedStatement pstmt = db.prepareStatement("SELECT * FROM reallynice.product");
//PreparedStatement pstmt = db.prepareStatement("SELECT * FROM reallynice.username WHERE emailaddress='user#me.com' AND passwordhash='megahashstring'");
rs = pstmt.executeQuery();
System.out.println("Rows returned\t" + rs.getRow());
if(rs.getRow() < 1)
return null;
int id = rs.getInt("uid");
String name = rs.getString("name");
String emailaddress = rs.getString("emailaddress");
String password = rs.getString("passwordhash");
boolean isAdmin = false;
pstmt = db.prepareStatement("SELECT * FROM reallnice.admin WHERE uid= ?");
pstmt.setInt(1, id);
rs = pstmt.executeQuery();
if(rs.getMetaData().getColumnCount() > 0)
isAdmin = true;
return new User(id,isAdmin,name,emailaddress,password);
} catch(Exception ex) {
System.out.println(ex);
}
return null;
}
I have also included the other queries I have tried for this.
Whenever I see someone having an experience like this: "no matter what I try this statement returns 0 rows," there are two possible reasons that come immediately to mind:
1) You aren't using the database you think you are. Derby's connection URL, if you say ";create=true", will quite happily make a new, empty database when you connect, if it doesn't find an existing database in the location you expect. This sort of problem arises from a confusion over where the databases are created; a database with a relative name will be created in whatever directory turns out to the be derby.system.home of the Derby instance that gets that connection URL. So check to see if you are using a different current working directory, or for some other reason are connecting to a different database than you think you are.
2) You aren't using the schema you think you are. Derby will quite happily create multiple schemas, and each schema has a separate set of tables, so if you are initially connecting as user A, and then later connect as user B, and don't issue SET SCHEMA, then user A and user B have completely separate sets of tables and so you won't be accessing the tables that you think you are. So check to see if you are connecting as the same user and using the same schema when you connect to the database.
Try changing how you display your logging statement
System.out.println("Rows returned\t" + rs.getRow());
getRow() returns the current row number, not how many records were returned. In order to user getRow() to count the number of entries in the result set you would need to move the pointer of the result set to the last entry.
You have also, not called next() yet, which means you aren't pointing at anything (and most likely the reason you always see 0 as the number). Try using
while(rs.next()){ //go through the entire ResultSet}
or
if(rs.next()) { //access the first record in the ResultSet}
So over all, if you change your code to something like the following you may have better results.
rs = pstmt.executeQuery();
if(rs.next()){
System.out.println("Processing Row " + rs.getRow());
//continue on
}else{
System.out.println("No Records");
}
If you have set your table where the username is a unique key, you can be assured this will return 0 or 1 row. Otherwise use the while() option instead of if()
EDIT::
Also as a side note, because you are not calling next()
if(rs.getRow() < 1)
return null;
will always be 0, which returns null from your method.

Categories

Resources