Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java implicit try-with-resources

I am wondering if the following code uses the try-with-resources correctly.

try (ResultSet rs = new QueryBuilder(connection, tableName(), getPaths(), searchQuery()).add(constraint).build().executeQuery()) {
    while (rs.next()) {
        beans.add(createBean(rs));
    }
}

The arguments are not important, the only important thing is:

  • new QueryBuilder().build(); returns a PreparedStatement.

I completely understand that rs will be closed, but will the PreparedStatement also be closed, and if so, for what reason? Because the ResultSet closes or because of the try-with-resources?

like image 206
skiwi Avatar asked Jul 22 '13 12:07

skiwi


People also ask

Can we use try-with-resources in Java?

The try -with-resources statement is a try statement that declares one or more resources. A resource is an object that must be closed after the program is finished with it. The try -with-resources statement ensures that each resource is closed at the end of the statement. Any object that implements java.

Can we use finally with try-with-resources?

You can use catch and finally blocks with try-with-resources statement just like an ordinary try statement.

What happens if try-with-resources throws an exception?

If an exception is thrown from within a Java try-with-resources block, any resource opened inside the parentheses of the try block will still get closed automatically. The throwing of the exception will force the execution to leave the try block, and this will force the automatic closing of the resource.

Does Java 8 support try resources?

It is possible, and in fact common, for a base class to implement AutoCloseable even though not all of its subclasses or instances will hold releasable resources.


4 Answers

PreparedStatement#close() will automatically close any associated result sets but the reverse is not true because statements are reusable after their result sets are closed.

Look at the javadoc of ResultSet#close():

Note: A ResultSet object is automatically closed by the Statement object that generated it when that Statement object is closed

And then Statement#close():

Note: When a Statement object is closed, its current ResultSet object, if one exists, is also closed.

This usage looks very crappy to me :

ResultSet rs=conn.createStatement().executeQuery();

If executed enough times it will leak all of the available cursors, because cursors are associated with the Statement not with ResultSet.

Hence to close the underlying PreparedStatement with try-with-resources statement , just declare it within the try statement :

A try-with-resources statement is parameterized with variables (known as resources) that are initialized before execution of the try block and closed automatically.

Look at this answer from assylias, declare the PreparedStatement as well as ResultSet inside the try statement.

Since you are not looking for a memory leak, but a resource leak . The memory of the PreparedStatement will be collected eventually and it's memory freed, as it is not referenced anymore after execution of your method given the way it is initialized , however, the resources hold by the Statement is not closed .

like image 134
AllTooSir Avatar answered Oct 25 '22 21:10

AllTooSir


You can include several resources in the try, and they will all be closed - which is necessary if you want the PreparedStatement to be closed:

try (PreparedStatement ps = new QueryBuilder(connection, tableName(), getPaths(), searchQuery()).add(constraint).build();
        ResultSet rs = ps.executeQuery();) {
    while (rs.next()) {
        beans.add(createBean(rs));
    }
}
like image 36
assylias Avatar answered Oct 25 '22 23:10

assylias


According to the documentation here - tryResourceClose, as I read it, it is specific to resources that are declared.

The try-with-resources statement is a try statement that declares one or more resources.

Reading further down you see:

You may declare one or more resources in a try-with-resources statement. The following example retrieves the names of the files packaged in the zip file zipFileName and creates a text file that contains the names of these files:

 try (
      java.util.zip.ZipFile zf =
         new java.util.zip.ZipFile(zipFileName);
    java.io.BufferedWriter writer = 
        java.nio.file.Files.newBufferedWriter(outputFilePath, charset)
  ) {

I suggest the correct answer to you issue is the following:

try{
  PreparedStatement statement = new QueryBuilder(connection, tableName(), getPaths(), searchQuery())
       .add(constraint).build();
  ResultSet rs = statement.executeQuery()) 
}
like image 36
John B Avatar answered Oct 25 '22 21:10

John B


As you correctly stated, rs will be closed. This means actually that the close() method will be invoked on rs. So the try-with-ressource statement doesn't explictly close the PreparedStatement in your case.

If it's closed otherwise (in context of the rs.close()) is kind of hard to say without knowing the implementation ;-)

EDIT

As @TheNewIdiot correctly found out, your PreparedStatement won't be closed.

like image 35
André Stannek Avatar answered Oct 25 '22 22:10

André Stannek