8

Consider the following situation :

try (ResultSet resultSet = DriverManager.getConnection("jdbc:...", "user", "pass")
                                      .createStatement().executeQuery(sql)) {    
                    .
                    .
                    .
}

This is a situation where three resources have been created inside try resources: a Connection, a Statement, and a ResultSet.

What will happen to these three resources after the try block ends? Will they all be closed, even if they haven't any reference to them, or will only the resultSet be closed?

Is this safe to declare AutoCloseable resources without any reference to them in try with resource blocks?

Mohsen R. Agdam
  • 364
  • 3
  • 12
  • At least closely-related, possibly dupe: https://stackoverflow.com/questions/28276423/is-it-necessary-to-close-each-nested-outputstream-and-writer-separately/28276498#28276498 – T.J. Crowder Nov 30 '20 at 14:03

5 Answers5

9

Only the ResultSet will be closed. If you want multiple resources to be closed, you need to declare them separately:

try (Connection conn  = DriverManager.getConnection("jdbc:...", "user", "pass");
     Statement stmt = conn.createStatement();
     ResultSet resultSet = stmt.executeQuery(sql)) {    

     // do stuff...
}
Mureinik
  • 297,002
  • 52
  • 306
  • 350
7

Is this safe to declare AutoCloseable resources without any reference to them in try with resource blocks?

Yes and no. Those resources that were not assigned to resource variables won't be auto-closed by this code. Therefore:

  • "Yes" those resources will still be "safe" to use via operations on the ResultSet within the try block.
  • "No" those resources will leak, and that is liable to cause problems later on.

Since I interpret "safe" to mean both of those things, my considered answer is "No" it is not safe.

The only resources that will be auto-closed by the try-with-resources are the ones that are assigned to the resource variables. So the correct way to write that would be:

try (Connection connection = DriverManager.getConnection(...);
     Statement statement = connection.createStatement();
     ResultSet resultSet = statement.executeQuery(sql)) {
    // ...   
}

Note that the resource variables are auto-closed in the reverse order that they are declared.


Note that you can often get away with it. For example:

try (BufferedWriter bw = new BufferedWriter(new FileWriter(...)) {
    // ...   
}

is likely to be OK except under extremely unusual circumstances. When bw is auto-closed, its close() method calls close() on the FileWriter. The only case where this might leak a resource is when the BufferedWriter creation / construction fails. I think that is only possible if you are unlucky enough to get an OOME at that point. (And the OOME may well trigger a full garbage collection which will find and close the dropped resource anyway ...)

Stephen C
  • 698,415
  • 94
  • 811
  • 1,216
  • I think, based on the OP, that the answer is actually "Yes, it's safe", because it is true that the resources you open in this manner are not referenced inside the try block. But it is still safe because there is no need to reference them, just to declare them in the proper order. – RealSkeptic Nov 29 '20 at 12:36
  • The OP included two parts. One is "what will happen to these resources". The other was "If we declare the autoclosable resources [in the try], but not reference them [in the try body], will that be safe"? That was a question about a solution to the situation - exactly the solution you provided. – RealSkeptic Nov 29 '20 at 12:43
  • I was just looking for a more concise way to get the `resultSet` since I don't have anything to do with the `statement` and `connection` in the `try` body. but for the sake of autoCloseablity, it seems I have to define a variable for each one. – Mohsen R. Agdam Nov 29 '20 at 13:30
  • Yup. The "concise" way leaks resources. – Stephen C Nov 29 '20 at 14:00
5

You can do that with multiple resources too:

try (Connection c = DriverManager.getConnection("jdbc:...", "user", "pass");
     Statement s = c.createStatement();
     ResultSet resultSet = s.executeQuery(sql)) {
   //...
}

All variables that implement the AutoClosable interface get closed after the execution of the try block.

f1sh
  • 11,489
  • 3
  • 25
  • 51
4

Your code will only close the ResultSet, leaving the Connection and Statement open.

In your case, if you do not reuse the Connection nor the Statement, to close them all, note the try-with-resources supports multiple statements:

try (Connection connection = DriverManager.getConnection(...);
     Statement statement = connection.createStatement();
     ResultSet resultSet = statement.executeQuery(...)) {
    ...
}
spongebob
  • 8,370
  • 15
  • 50
  • 83
3

It wont close all the three resources since you have not created each resource separately followed by semicolon. If you create each resource separately it will be closed. ex:

 try (
            Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/", "root", "password");
            Statement stmt = connection.createStatement();
            ResultSet rs = stmt.executeQuery(sql)
            ){}

this will work because try-with-resources statement closes the resources in the reverse order from which they were opened. We can use objects which implements AutoCloseable inside try with resource if it doesnt implement then it needs to be closed inside the final black. you can check more details here https://docs.oracle.com/javase/tutorial/essential/exceptions/tryResourceClose.html

Arun Prasat
  • 340
  • 1
  • 9