Java implicit try-with-resources

2019-04-19 08:25发布

问题:

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?

回答1:

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 .



回答2:

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));
    }
}


回答3:

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()) 
}


回答4:

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.