Java implicit try-with-resources

2019-04-19 08:18发布

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?

4条回答
Fickle 薄情
2楼-- · 2019-04-19 08:34

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));
    }
}
查看更多
Animai°情兽
3楼-- · 2019-04-19 08:39

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.

查看更多
小情绪 Triste *
4楼-- · 2019-04-19 08:47

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()) 
}
查看更多
聊天终结者
5楼-- · 2019-04-19 08:50

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 .

查看更多
登录 后发表回答