Good Code
The good version makes resource ownership visible. Connections, statements, and result sets are closed even when the query or mapping throws.
Lesson 05
Use try-with-resources for files, connections, statements, and result sets so cleanup happens on success and failure.
public Optional<User> findByEmail(String email) throws SQLException {
String sql = "select id, email, name from users where email = ?";
try (
Connection connection = dataSource.getConnection();
PreparedStatement statement = connection.prepareStatement(sql)
) {
statement.setString(1, email);
try (ResultSet rows = statement.executeQuery()) {
if (!rows.next()) {
return Optional.empty();
}
return Optional.of(mapUser(rows));
}
}
}public User findByEmail(String email) throws SQLException {
Connection connection = dataSource.getConnection();
PreparedStatement statement = connection.prepareStatement(
"select * from users where email = '" + email + "'"
);
ResultSet rows = statement.executeQuery();
if (!rows.next()) {
return null;
}
connection.close();
return mapUser(rows);
}The good version makes resource ownership visible. Connections, statements, and result sets are closed even when the query or mapping throws.
The bad version manually closes only one resource on the happy path, skips cleanup on early returns, and mixes unsafe SQL construction into the same method.