Successfully close the instances of Connection
, Statement
, ResultSet
and handle well all the errors that may result using the typical pattern of try-catch-finally
is not easy. And if you succeed, the code is not elegant.
For example, even if you correct your problem immediately to move the definition of the variables outside the try
, the code still has at least one design flaw.
Let's say that the code is corrected in this way (and by the way, reading the comments under the response of @Dev Joel, it is necessary to clarify that it is necessary to initialize the variables to some value as null
. that, the Java compiler can not guarantee that the variables have some assigned value when they are used in the finally
block, and that's why they complain, and in fact, this point is linked to the defect that I'm going to show) :
String showTableSQL = "SELECT * FROM llamadas";
// inicializar las variables sí es necesario aquí.
Connection conn = null;
Statement st = null;
ResultSet rs = null;
try {
conn = conexion.getConnection(); // <-- ¿qué pasa si un error sucede aquí?
st = conn.createStatement();
rs = st.executeQuery(showTableSQL);
// ...
} catch (SQLException errorSQL) {
errorSQL.printStackTrace();
}
finally { // Cerramos las conexiones, en orden inverso a su apertura
try { rs.close(); } catch (Exception errorRS) { errorRS.printStackTrace(); }
try { st.close(); } catch (Exception errorST) { errorST.printStackTrace(); }
try { conn.close(); } catch (Exception errorCONN) { errorCONN.printStackTrace(); }
}
Now let's imagine that some error happens in this sentence:
conn = conexion.getConnection();
That causes that st
and rs
are with values null
, and now, when the block finally
is executed, both rs.close();
as st.close()
are going to launch a NullPointerException
that could be have avoided.
So the correct way to handle block finally
is to check if the variables are at null
before trying to close them:
finally { // Cerramos las conexiones, en orden inverso a su apertura
try { if (rs != null) rs.close(); } catch (Exception errorRS) { errorRS.printStackTrace(); }
try { if (st != null) st.close(); } catch (Exception errorST) { errorST.printStackTrace(); }
try { if (conn != null) conn.close(); } catch (Exception errorCONN) { errorCONN.printStackTrace(); }
}
A better alternative
The above simply illustrates that it is easy to make mistakes with this type of code due to its complexity. And even if you manage to do it right, the complexity obscures the true purpose of the code.
For this reason, starting with Java 7, there is a better way to handle this type of situation, using try-with-resources
. Using this technique, note how you can simplify your code and how clear it is:
String showTableSQL = "SELECT * FROM llamadas";
try (Connection conn = conexion.getConnection();
Statement st = conn.createStatement();
ResultSet rs = st.executeQuery(showTableSQL)) {
// ...
} catch (SQLException e) {
e.printStackTrace();
}
If you read the documentation , you'll see that this simplified code structure automatically handles the following aspects:
- All
.close()
are automatically executed at the end of block try
and in the reverse order of the opening of the different instances.
- If an error occurs with one or more of the
.close()
, those exceptions do not replace the main exception, if any.
- Related to the previous point, if more than one error occurs, either in the body of the
try
or with the .close()
, e.printStackTrace()
includes the information of all the errors automatically.