Java - Can't use ResultSet after connection close - java

I have a problem with closing a connection to MySQL.
I'm getting the error:
java.sql.SQLException: Operation not allowed after ResultSet closed
My code:
public static ResultSet sqlquery (String query)
{
ResultSet rs=null;
Connection connection=null;
Statement st=null;
try{
Class.forName("com.mysql.jdbc.Driver");
connection = DriverManager.getConnection("databaseadress","username","password");
st = connection.createStatement();
rs = st.executeQuery(query);
}catch(SQLException e){System.out.println("SQL error: " + e);}
catch(Exception e){System.out.println("Error: " + e);}
finally {
try{
if(rs != null) rs.close();
if(st!= null) st.close();
if(connection != null) connection.close();
}catch(SQLException e){System.out.println("SQL error : " + e);}
}
return rs;
}

JDBC doesn't bring back all the results of a query in a ResultSet, because there may be too many of them to fetch them all eagerly. Instead it gives you something you can use to retrieve the results, but which goes away when the connection closes. So when you pass it back from your method after closing the database connection, nothing else can use it.
What you can do instead is to have this method use the resultSet to populate an object or a collection of objects, and pass that populated object back.
If you change your code to pass in a rowMapper (that takes a resultSet and passes back an object populated with the current row in the resultset), and use that to populate a container object that you pass back, then you'll have something as reusable as what what you've written, but which actually works because it doesn't depend on having the connection held open after the call is finished.
Here's your example code rewritten to use the rowmapper, get rid of some unnecessary exception-catching, and fix a bug that will prevent the connection from getting closed in some cases:
public static List<T> sqlquery (String query, RowMapper<T> rowMapper) throws SQLException
{
Connection connection=null;
Statement st=null;
ResultSet rs=null;
// don't need Class.forName anymore with type4 driver
connection = DriverManager.getConnection("databaseadress","username","password");
st = connection.createStatement();
rs = st.executeQuery(query);
List<T> list = new ArrayList<T>();
while (rs.next()) {
list.add(rowMapper.mapRow(rs));
}
// don't let exception thrown on close of
// statement or resultset prevent the
// connection from getting closed
if(rs != null)
try {rs.close()} catch (SQLException e){log.info(e);}
if(st!= null)
try {st.close()} catch (SQLException e){log.info(e);}
if(connection != null)
try {connection.close()} catch (SQLException e){log.info(e);}
return list;
}
If you don't catch each exception thrown on close individually, as shown above, you risk failing to close the connection if either the statement or the resultSet throw an exception on close.
This is similar to what spring-jdbc does, it defines a RowMapper as:
public interface RowMapper<T> {
T mapRow(ResultSet, int rowNum) throws SQLException;
}
The next step is going to be parameterizing your queries so you don't have to
surround parameter values in quotes or worry about sql injection. See this answer for an example of how spring-jdbc handles this. The long term answer here is, it would be better to adopt spring-jdbc or something similar to it than to reinvent it piecemeal.

This is the way JDBC works. In your code you closed the ResultSet and the Connection, after which the ResultSet is no longer usable. If you want it to be usable you must leave it (and the Connection) open.
However, if you return the ResultSet, you should refactor your code so the calling method provides the Connection.

RowSetFactory factory = RowSetProvider.newFactory();
CachedRowSet rowset = factory.createCachedRowSet();
rowset.populate(ResultSet data)
/* now you can close you connection and prepare statement*/

Once the connection is closed you can no longer use any of the resources (statements, prepared statements, result sets), all of them are automatically closed. So you need to do all of your processing while the resources are open.
Try filling and returning a DTO, this way you can have the data you need without keeping a connection alive.

Related

null check in try-with-resources

I've got the following code:
try (Connection connection = getConnection();
PreparedStatement preparedStatement = connection.prepareStatement(someSql)) {//stuff}
How do I check that connection is not null here?
Also, I got a method which returns a PreparedStatement like this:
private PreparedStatement getPreparedStatement(Connection connection)
throws SQLException {
PreparedStatement preparedStatement = connection.prepareStatement(SQL);
preparedStatement.setString(1, "someString");
return preparedStatement;
}
this gets called within a method that uses the following try with resources:
try (Connection connection = getConnection();
PreparedStatement preparedStatement =
getPreparedStatement(connection)) {//stuff}
Now I would assume the prepared statement will be autoclosed, because it gets initiated in the try with resources. But SonarCloud says that i should use try with resources in the getPreparedStatement-method or close that PreparedStatement in a finally block. Is this a wrong finding by SonarCloud or is there a better way to do it?
getConnection should throw an exception instead of returning null. Returning null is not nice. SonarCloud seems to be wanting you to put a try-with-resource opening ingetPreparedStatement(must include thesetString`) and closing in the calling method, which of course you can't do as such.
The best approach is the Execute Around idiom. Instead of getPreparedStatement returning a PreparedStatement pass in a lambda (typically) to be executed. The resource can then be closed in a try-with-resource statement cleanly.
/*** NICE ***/
// Function instead of Consumer would allow the method to return a value.
private void prepared(
Connection connection, Consumer<PreparedStatement> op
) throws SQLException {
// Might want to add getConnection in here too, perhaps.
try (
PreparedStatement statement =
connection.prepareStatement(SQL)
) {
statement.setString(1, "someString");
op.accept(statement);
}
}
Used as:
try (Connection connection = getConnection()) {
prepared(connection, statement -> {
// blah, blah, blah
});
}
The hacky alternative is to include a try statement within getPreparedStatement that only closed in error conditions.
/*** HACKY ***/
private PreparedStatement prepared(
Connection connection
) throws SQLException {
boolean success = false;
PreparedStatement statement =
connection.prepareStatement(SQL);
try {
statement.setString(1, "someString");
success = true;
return preparedStatement;
} finally {
if (!success) {
statement.close();
}
}
}
If you desperately want getConnection to return null (don't) and use a single two-entry try-with-resource, then the conditional operator works.
try (
Connection connection = getConnection();
PreparedStatement statement =
connection==null ? null : connection.prepareStatement(SQL)
) {
Easiest approch and IMO the most readable one would be to use 2 try-with-resources blocks. Even sonar has an built-in exception from disallowing nested try-catch-blocks for exactly this use case...
try (Connection conn = getConnection()) {
if (conn != null) {
try (PreparedStatement stmt = ...) {
// do stuff
}
}
}
If do stuff is lengthy, it could and should be refactored into a separate method (possibly including the inner try-with-resources).

Resultset is empty when I close preparedStatement

I have such a method:
private static ResultSet select (Connection connection, String query) {
PreparedStatement selectQuery = null;
ResultSet resultSet = null;
try {
selectQuery = connection.prepareStatement(query);
resultSet = selectQuery.executeQuery();
selectQuery.close();
} catch (SQLException e) {
System.out.println(e);
}
return resultSet;
}
The thing is that the resultSet is always empty when I close the preparedStatement.
If I comment out the line with clothing preparedStatement //selectQuery.close(); everything is fine.
I close it after assigning value to the resultSet. So why it's empty?
A ResultSet is associated with an executed Statement. Close the statement and the resultset, with any data in it is cleared.
You need to process the resultset before you close the statement, so your approach will not work.
You have to iterate through the ResultSet. You have an high level example right here:
try{
// execute the query
ResultSet rs = st.executeQuery(query);
// iterate through the result set
while (rs.next())
{
// Replace with your data
int id = rs.getInt("id");
String name = rs.getString("name");
// do stuff with the result set, add to a List of objects (for example)
}
selectQuery.close();
}catch(SQLException e) {
System.out.println(e);
}
You have not to close the statement before having retrieved the data of the resultset otherwise these may be not accessible.
When you call this method, its ResultSet objects are closed.
So, only when you are finished using a Statement, call the Statement.close() method.
The close should be performed in a finally statement.
In this way you are ensure that you don't worry to wonder when close it.
With your actual code :
private static ResultSet select (Connection connection, String query) {
PreparedStatement selectQuery = null;
ResultSet resultSet = null;
try {
selectQuery = connection.prepareStatement(query);
resultSet = selectQuery.executeQuery();
} catch (SQLException e) {
System.out.println(e);
}
finally {
if (selectQuery != null) { selectQuery.close(); }
}
return resultSet;
}
}
A better alternative is using a try-with-resources statement :
try (Statement stmt = con.createStatement()) {
// ...
}
Because the javadoc says so:
Note: When a Statement object is closed, its current ResultSet object, if one exists, is also closed.
Rationale: The stated behavior of Statement.close() is to release all resources. One of those resources is the server-side cursor for reading the results. But if you release that, then the ResultSet has nothing to pull data from.
I'm curious how you are determining that the (closed) ResultSet is "empty". It looks like all operations on a closed ResultSet (apart from close()) ought to throw an exception.

new jre7 try block resources

If I do something like
try (
Connection conn = Database.getConnection();
PreparedStatement ps = conn.prepareStatement("SELECT * FROM table WHERE something = ? LIMIT 1");
) {
ps.setString(1, "hello world");
ResultSet results = ps.executeQuery();
if(results.next()) {
// blah
}
} catch(SQLException e) {
e.printStackTrace();
}
Will the ResultSet still be closed when the PreparedStatement is closed, or will I still have to explicitly close the ResultSet also?
As per javax.sql.Statement.close() method's JavaDoc:
Note:When a Statement object is closed, its current ResultSet object, if one exists, is also closed.
So, answering your question - yes, ResultSet will be automatically closed in your case, because related Statement is closed in try-with-resources block.
However, please note that explicitly closing ResultSets is a good practice which is recommended to follow, so your modified code following good practices would look like:
try (
Connection conn = Database.getConnection();
PreparedStatement ps = prepareStatement(conn, "SELECT * FROM table WHERE something = ? LIMIT 1", param);
ResultSet results = ps.executeQuery();
) {
if(results.next()) {
// blah
}
} catch(SQLException e) {
e.printStackTrace();
}
private static PreparedStatement prepareStatement(Connection connection, String sql, String param) throws SQLException {
final PreparedStatement ps = conn.prepareStatement(sql);
ps.setString(1, param);
return ps;
}
Always As a good practice, try to close your ResultSets and PreparedStatements. In a finally block. Every single time , managing exceptions, so you won't leave resources unattended (is a common source of leaks).
Unless you inject them to the method, hence the calling method probably needs them.
EDIT: Stand corrected. If resultset was created as try-with-resource, will die with your PS.

Operation not allowed after ResultSet closed

Allright been trying to figure this out the last 2 days.
Statement statement = con.createStatement();
String query = "SELECT * FROM sell";
ResultSet rs = query(query);
while (rs.next()){//<--- I get there operation error here
This is the query method.
public static ResultSet query(String s) throws SQLException {
try {
if (s.toLowerCase().startsWith("select")) {
if(stm == null) {
createConnection();
}
ResultSet rs = stm.executeQuery(s);
return rs;
} else {
if(stm == null) {
createConnection();
}
stm.executeUpdate(s);
}
return null;
} catch (Exception e) {
e.printStackTrace();
con = null;
stm = null;
}
return null;
}
How can I fix this error?
It's hard to be sure just from the code you've posted, but I suspect that the ResultSet is inadvertently getting closed (or stm is getting reused) inside the body of the while loop. This would trigger the exception at the start of the following iteration.
Additionally, you need to make sure there are no other threads in your application that could potentially be using the same DB connection or stm object.
IMHO, you should do everything you need with your ResultSet before you close your connection.
there are few things you need to fix. Opening a connection, running a query to get the rs, closing it, and closing the connection all should be done in the same function scope as far as possible. from your code, you seem to use the "con" variable as a global variable, which could potentially cause a problem. you are not closing the stm object. or the rs object. this code does not run for too long, even if it has no errors. Your code should be like this:
if (stringUtils.isBlank(sql)){
throw new IllegalArgumentsException ("SQL statement is required");
}
Connection con = null;
PreparedStatement ps =null;
Resultset rs = null;
try{
con = getConnection();
ps = con.preparestatement(sql);
rs = ps.executeQuery();
processResults(rs);
close(rs);
close(ps);
close(con);
}catch (Execption e){
log.Exception ("Error in: {}", sql, e);
throw new RuntimeException (e);
}finally{
close(rs);
close(ps);
close(con);
}
use another Statement object in inner loop
Like
Statement st,st1;
st=con.createStatement();
st1=con.createStatement();
//in Inner loop
while(<<your code>>)
{
st1.executeQuery(<<your query>>);
}
I know this is a few years late, but I've found that synchronizing the db methods usually get rid of this problem.

Is this jdbc resource closed?

Looking back over my code I find that I occasionaly have written:
ResultSet rs = conn.createStatement().executeQuery("select * from main");
//snip
rs.close();
and sometimes I've written
Statement st = conn.createStatement();
ResultSet rs = st.executeQuery("Select * from main");
//snip
rs.close();
st.close();
In the second code segment, it's more obvious that the Statement is closed, but is it also closed in the first one? conn.createStatement() returns a statement object, but when it's instantiated like that I don't see any easy way to close it after I'm done. Should I just rewrite the various bits of code to use method #2?
A good practice is to put the rs.close() and st.close() in your finally clause.
Statement st;
ResultSet rs;
try {
st = connection.createStatement(...);
rs = st.executeQuery();
}
catch (JdbcErrorsExceptionsAndFoo exception) {
// yadda yadda
}
finally {
if (rs!= null) {
rs.close();
}
if (st != null) {
st.close();
}
}
The Statement will be automatically closed when it is garbage collected, but you need to explicitly close it if you want to free the resources as soon as you're done with them.
Note, however, that the reverse actually does work. That is, closing a Statement also closes the ResultSet associated with it.
if statement contains a resultset, gc does not collect it. you can use the second method or use aspects or filters to auto close them.

Categories

Resources