Reusing ResultSet - java

I need to run several queries in a row
Statement st = cnx.createStatement();
ResultSet rs = st.executeQuery( "SELECT [good stuff]");
// do something smart with rs
rs = st.execute( "SELECT [better stuff]");
// do something smarter with rs
rs = st.execute( "SELECT [best stuff]");
// you got it
try{ rs.close();} catch( SQLException ignore){};
try{ st.close();} catch( SQLException ignore){};
Is this a problem that the first two ResultSet are not properly closed or is it implicitely done during garbage collection?

As soon as you execute the 2nd query, the previous ResultSet is automatically closed. And as far as Garbage Collection is concerned, you don't have to worry about that. You can just have a stmt.close() at the end that's all. It will automatically close all the related ResultSet objects.
Take a look at : - ResultSet#close documentation, which says that: -
A ResultSet object is automatically closed by the Statement object
that generated it when that Statement object is closed, re-executed,
or is used to retrieve the next result from a sequence of multiple
results.
If you want to test, whether your resultset gets closed or not, you can use a while loop to iterate over the result set and inside the while loop, create another query and assign it to same result set. You will see that an Exception will be thrown..
ResultSet res = stmt.executeQuery("SELECT * FROM sometable");
while (res.next()) {
res.getString(1);
// Closes the previous `ResultSet`
res = stmt.executeQuery("SELECT * FROM othertable");
}
So, in the above code, on the 2nd iteration, you will get an Exception: - Cannot perform operation after ResultSet is closed

I don't know what's your problem, but if you have some problems to run this code, you can try to close connection and open other to make the second query. Some database products, like SQLite, only admit one open connection. If you have any problem with database access, you should try that.

Related

getGeneratedKeys() returns an Empty ResultSet

Hello there and thanks for reading.
I'm trying to retrieve the ID of the newly inserted data, but I always get an empty ResultSet.
Connection con = main.getCon();
String sqlCommand = "Insert Into Relations(name,explanation) values(?,?)";
PreparedStatement state =
con.prepareStatement(sqlCommand,Statement.RETURN_GENERATED_KEYS);
state.setString(1,name.getText());
state.setString(2,explanation.getText());
int affectedRows = state.executeUpdate();
assert (affectedRows>0);
ResultSet rs = state.getGeneratedKeys();
assert rs.next();
int instertedID= rs.getInt("ID");
Not sure what's wrong with it. Checked different samples online, but couldn't figure out what's my mistake.
I also tried it with Statement, but no luck with that either.
Point 1: the code runs smoothly and my data in inserted into the database.
Point 2: there are examples online for this very case, you can check it here:
https://www.baeldung.com/jdbc-returning-generated-keys
I just realized that my ResultSet wasn't empty, I had problem with using my debugger and that's why I thought it was empty.
As Mark Rotteveel mentioned in a comment, the problem was with "assert" statement.
The problem is your use of assert rs.next(). Assertions in Java are intended for checking invariants (eg during testing), but when you normally run Java, assert statements are not executed, they are only executed when explicitly enabling this with the -ea commandline option.
As a result, rs.next() is not called, so your result set is still positioned before the first row when you call rs.getInt(1). Instead use if (rs.next()) { ... }.
This is DB engine dependent. Some tips:
JDBC is low-level and not appropriate to program with
It's a complicated API. Use something that makes it easier: JDBI, or JOOQ. They may have abstractions over insertion that takes care of this stuff for you.
Some DB engines require that you list the column name
Try:
con.prepareStatement(sqlCommand, new String[] {"UNID"});
Some DB engines will only return generated values as direct resultset
Don't call .executeUpdate(); instead, call .executeQuery() which returns a ResultSet; check that one.
Something else
Post the exact table structure and DB engine you're working with if the above doesn't help.
Your code is broken
You can't create resource objects (once that must be closed) unless you do so safely, and you're not doing so safely. Use try-with-resources:
String sql = "INSERT INTO relations(name, explanation) VALUES (?, ?)";
try (Connection con = main.getCon();
PreparedStatement ps = con.prepareStatement(sql, new String[] {"unid"})) {
state.setString(1, name.getText());
state.setString(2, explanation.getText());
try (ResultSet rs = state.executeQuery()) {
if (!rs.next()) throw new SQLException("insert didn't return autogen?");
System.out.println(rs.getInt(1));
}
}
ResultSets, Statements, PreparedStatements, and Connections are all resources (must be closed!) - if you want to store one of those things in a field, you can do that, but only if the class that contains this field is itself a resource: It must have a close() method, it must implement AutoClosable, and you can then only make instances of this class with try-with-resources as above.
Failure to adhere to these rules means your app seems to work, but is leaking resources as it runs, thus, if you let it run long enough, it will start crashing. Also, your DB engine will grind to a halt as more and more connections are left open, stuck forever.
change the last line of code to this because the DBMS you are using may not support the getting value by column name so pass the index of that column:
int instertedID = rs.getInt(1);
String sqlCommand = "Insert Into Relations (name, explanation) values(?, ?)";
PreparedStatement state = con.prepareStatement(sqlCommand, Statement.RETURN_GENERATED_KEYS);
state.setString(1,name.getText());
state.setString(2,explanation.getText());
state.executeUpdate();
ResultSet resultSet = state.getGeneratedKeys();
if(resultSet.next()) {
System.out.println(resultSet.getInt(1)); //Indicate the corresponding column index value.
}

Try with resource (PreparedStatements)

So I was wondering which method is prefered, I will be reading each line from a file and storing it into onto my database. I will have a lot of prepared statements as I will be doing a lof of selects (checking if entry exists to grab an id value from table), or inserts when it doesn't exist. These kind of sql will be performed like 5 times as I will be inserting into 5 different tables.
So I wasn't sure if it's ok to create a lot of prepared statements as I will be leaving a lot of opened connections, however most statements will be used while I'm reading from the file. Or because I have a lot of preparedstatements create them inside the while loop and have them in their own try resource block so I don't leave too many opened connections?
try {PreparedStatement stmt1 = connection.PrepareStatement(Sql);
PreparedStatement stmt2 = connection.PrepareStatement(Sql);
PreparedStatement stmt3 = connection.PrepareStatement(Sql);
PreparedStatement stmt4 = connection.PrepareStatement(Sql);
etc . . . )
while read line from file {
//etc
}
}
Or is this prefered:
while read line from file {
try {PreparedStatement stmt1 = connection.PrepareStatement(Sql);
PreparedStatement stmt2 = connection.PrepareStatement(Sql))
// stmt1.setint(1, blah)
// stmt1.execute()
//etc
}
try {PreparedStatement stmt3 = connection.PrepareStatement(Sql);
PreparedStatement stmt4 = connection.PrepareStatement(Sql))
// stmt1.setint(2, blah)
// stmt1.execute()
//etc
}
}
There is no problem for connection in either way as you are using the same connection object.
However, the second way is not recommended at all because you will be compiling the same SQLs again and again. A sheer waste of computing power and unnecessary pressure on garbage collection!
I would use the first variant. The second one openes and closes the PreparedStatement for each line, which will be slower than leaving them open all the time, especially if you have a lot of lines to process. Preparing the statement itsself is also not a cheap action.

iterative update to a jdbc derby using a ResultSet [duplicate]

This question already has an answer here:
Why do I get java.sql.SQLException: ResultSet not open. Operation 'next' not permitted. java derby database?
(1 answer)
Closed 6 years ago.
In connection with another programming project, I am prototyping a JDBC project (with Netbeans, Java, and a Derby database). My program needs to iteratively update all the rows in a database table as follows:
There are three columns in the table: famousName, famousQuote, hashKey.
Originally, the famousQuote column contains a verbatim quote. I want to go down that column using a while loop, get the checksum of the ascii letters, bitwise "AND" with the hashKey value, and then replace the verbatim quote with an "encrypted" value.
As of right now, I try to extract the verbatim famousQuote using a ResultSet object, perform necessary encryption, and then an SQL statement that updates the value. All of this takes place in a while(rs.next()) loop as follows:
(pseudo-code): //all necessary database connections and variable declared here.
String sqlStatement = "Select * FROM mainTable ORDER BY famousName";
ResultSet rs = stmt.executeQuery(sqlStatement);
while(rs.next()){
tempString1 = rs.getString("famousQuote");
tempString2 = rs.getString("hashKey");
tempString3 = EncryptionAlgorithm.EncryptStatement(tempString1, tempString2);
sqlStatement = "UPDATE maintable SET famousQuote=tempString3 WHERE hashKey=tempString2";
(note, there is a bit of pseudo-code regarding the WHERE part,
but I'm sure it's immaterial to the error message I'm getting.)
stmt.executeUpdate(sqlStatement);
}
This seemed like a good idea until the program started throwing errors such as:
ResultSet not open. Operation 'next' not permitted. Verify that autoCommit is off
I later read in the documentation (concerning ResultSet):
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.
Lastly, I guess I could try moving the ResultSet rs declaration inside the while loop, so that it would instantiate a new ResultSet object, but I'm pretty sure this would lose my place in the database (re-updating the first row perpetually).
I'm now at a standstill on how to iterate my way down the table rows, executing my EncryptionAlgorithm on each quote, and then updating the column values in place.
I apologize in advance if my JDBC is a little rusty, but something like this might do the trick:
// 'conn' is your JDBC connection
Statement stmt = conn.createStatement();
PreparedStatement update = conn.prepareStatement(
"UPDATE maintable SET famousQuote=tempString3 WHERE hashKey=tempString2";
String sqlStatement = "Select * FROM mainTable ORDER BY famousName";
ResultSet rs = stmt.executeQuery(sqlStatement);
while(rs.next()){
tempString1 = rs.getString("famousQuote");
tempString2 = rs.getString("hashKey");
tempString3 = EncryptionAlgorithm.EncryptStatement(tempString1, tempString2);
update.setObject(1, tempString3);
update.setObject(2, tempString2);
update.executeUpdate(sqlStatement);
}

Execute multiple queries using a single JDBC Statement object

In JDBC, can I use single Statement object to call executeQuery("") multiple times? Is it safe? Or should I close the statement object after each query, and create new object for executing another query.
E.G:
Connection con;
Statement s;
ResultSet rs;
ResultSet rs2;
try
{
con = getConnection();
// Initially I was creating the Statement object in an
// incorrect way. It was just intended to be a pseudocode.
// But too many answerers misinterpretted it wrongly. Sorry
// for that. I corrected the question now. Following is the
// wrong way, commented out now.
// s = con.prepareStatement();
// Following is the way which is correct and fits for my question.
s = con.createStatement();
try
{
rs = s.executeQuery(".......................");
// process the result set rs
}
finally
{
close(rs);
}
// I know what to do to rs here
// But I am asking, should I close the Statement s here? Or can I use it again for the next query?
try
{
rs2 = s.executeQuery(".......................");
// process the result set rs2
}
finally
{
close(rs2);
}
}
finally
{
close(s);
close(con);
}
Yes you can re-use a Statement(specifically a PreparedStatement) and should do so in general with JDBC. It would be inefficient & bad style if you didn't re-use your statement and immediately created another identical Statement object. As far as closing it, it would be appropriate to close it in a finally block, just as you are in this snippet.
For an example of what you're asking check out this link: jOOq Docs
I am not sure why you are asking. The API design and documentation show it is perfectly fine (and even intended) to reuse a Statement object for multiple execute, executeUpdate and executeQuery calls. If it wouldn't be allowed that would be explicitly documented in the Java doc (and likely the API would be different).
Furthermore the apidoc of Statement says:
All execution methods in the Statement interface implicitly close a statment's [sic] current ResultSet object if an open one exists.
This is an indication that you can use it multiple times.
TL;DR: Yes, you can call execute on single Statement object multiple times, as long as you realize that any previously opened ResultSet will be closed.
Your example incorrectly uses PreparedStatement, and you cannot (or: should not) be able to call any of the execute... methods accepting a String on a PreparedStatement:
SQLException - if [...] the method is called on a PreparedStatement or CallableStatement
But to answer for PreparedStatement as well: the whole purpose of a PreparedStatement is to precompile a statement with parameter placeholders and reuse it for multiple executions with different parameter values.
I can't find anything in the API docs that would state, that you shouldn't call executeQuery() on a given PreparedStatement instance more than once.
However your code does not close the PreparedStatement - a call to executeQuery() would throw a SQLException in that case - but the ResultSet that is returned by executeQuery(). A ResultSet is automatically closed, when you reexecute a PreparedStatement. Depending on your circumstances you should close it, when you don't need it anymore. I would close it, because i think it's bad style not to do so.
UPDATE Upps, I missed your comment between the two try blocks. If you close your PreparedStatement at this point, you shouldn't be able to call executeQuery() again without getting a SQLException.
A Prepared Statement tells the database to remember your query and to be prepared to accept parameterized variables to execute in that query. It's a lot like a stored procedure.
Prepared Statement accomplishes two main things:
It automatically escapes your query variables to help guard against SQL Injection.
It tells the database to remember the query and be ready to take variables.
Number 2 is important because it means the database only has to interpret your query once, and then it has the procedure ready to go. So it improves performance.
You should not close a prepared statement and/or the database connection in between execute calls. Doing so is incredibly in-efficient and it will cause more overhead than using a plain old Statement since you instruct the database each time to create a procedure and remember it. Even if the database is configured for "hot spots" and remembers your query anyways even if you close the PreparedStatement, you still incur network overhead as well as small processing time.
In short, keep the Connection and PreparedStatement open until you are done with them.
Edit: To comment on not returning a ResultSet from the execution, this is fine. executeQuery will return the ResultSet for whatever query just executed.
Firstly I am confused about your code
s = con.prepareStatement();
Is it work well?I can't find such function in JAVA API,at least one parameter is needed.Maybe you want to invoke this function
s = con.createStatement();
I just ran my code to access DB2 for twice with one single Statement instance without close it between two operation.It's work well.I think you can try it yourself too.
String sql = "";
String sql2 = "";
String driver = "com.ibm.db2.jcc.DB2Driver";
String url = "jdbc:db2://ip:port/DBNAME";
String user = "user";
String password = "password";
Class.forName(driver).newInstance();
Connection conn = DriverManager.getConnection(url, user, password);
Statement statement = conn.createStatement();
ResultSet resultSet = statement.executeQuery(sql);
int count = 0;
while (resultSet.next()) {
count++;
}
System.out.println("Result row count of query number one is: " + count);
count = 0;
resultSet = statement.executeQuery(sql2);
while (resultSet.next()) {
count++;
}
System.out.println("Result row count of query number two is: " + count);

Java generating query from resultSet and executing the new query

I am trying to use some query result to generate another query and execute the new query but that does not seem to work. The second query is not being executed. Can someone please tell me why? This is that part of the code.
Statment stmt = connnection.createStatement();
Statment stmt2 = connnection.createStatement();
ResultSet r = stmt.executeQuery("Select * from employees");
while (r.next()) {
String Str = "Select name from employees where employeeId = " + (r.getInt(3) + 1);
System.out.println(str);
query = stmt2.executeQuery(str);
System.out.println(query.getString(1));}
The right query seems to be generated, but just won't execute. Is there a reason why this is so. BTW "query" is declared as resultset.
Thanks
you can only have one statement executing at one moment in time against one database connection -- so you can either open another database connection and execute the second statement in the 2nd connection, or iterate through the resultset from first statement and store the employees database id's (e.g. in an array/collection) then close that statement and run the second one, this time retrieving the id's from the array/collection you saved them in.

Categories

Resources