¿Cómo debo usar try-with-resources con JDBC?

Tengo un método para obtener usuarios de una base de datos con JDBC:

public List getUser(int userId) { String sql = "SELECT id, name FROM users WHERE id = ?"; List users = new ArrayList(); try { Connection con = DriverManager.getConnection(myConnectionURL); PreparedStatement ps = con.prepareStatement(sql); ps.setInt(1, userId); ResultSet rs = ps.executeQuery(); while(rs.next()) { users.add(new User(rs.getInt("id"), rs.getString("name"))); } rs.close(); ps.close(); con.close(); } catch (SQLException e) { e.printStackTrace(); } return users; } 

¿Cómo debo usar Java 7 try-with-resources para mejorar este código?

He intentado con el siguiente código, pero usa muchos try , y no mejora mucho la legibilidad . ¿Debería usar try-with-resources de otra manera?

 public List getUser(int userId) { String sql = "SELECT id, name FROM users WHERE id = ?"; List users = new ArrayList(); try { try (Connection con = DriverManager.getConnection(myConnectionURL); PreparedStatement ps = con.prepareStatement(sql);) { ps.setInt(1, userId); try (ResultSet rs = ps.executeQuery();) { while(rs.next()) { users.add(new User(rs.getInt("id"), rs.getString("name"))); } } } } catch (SQLException e) { e.printStackTrace(); } return users; } 

    Me doy cuenta de que esto fue respondido hace mucho tiempo, pero quiero sugerir un enfoque adicional que evite el doble bloque nested try-with-resources.

     public List getUser(int userId) { try (Connection con = DriverManager.getConnection(myConnectionURL); PreparedStatement ps = createPreparedStatement(con, userId); ResultSet rs = ps.executeQuery()) { // process the resultset here, all resources will be cleaned up } catch (SQLException e) { e.printStackTrace(); } } private PreparedStatement createPreparedStatement(Connection con, int userId) throws SQLException { String sql = "SELECT id, username FROM users WHERE id = ?"; PreparedStatement ps = con.prepareStatement(sql); ps.setInt(1, userId); return ps; } 

    No hay necesidad de intentarlo en su ejemplo, por lo que al menos puede bajar de 3 a 2, y tampoco necesita cerrar ; al final de la lista de recursos. La ventaja de usar dos bloques de prueba es que todo su código está presente por adelantado para que no tenga que referirse a un método diferente:

     public List getUser(int userId) { String sql = "SELECT id, username FROM users WHERE id = ?"; List users = new ArrayList<>(); try (Connection con = DriverManager.getConnection(myConnectionURL); PreparedStatement ps = con.prepareStatement(sql)) { ps.setInt(1, userId); try (ResultSet rs = ps.executeQuery()) { while(rs.next()) { users.add(new User(rs.getInt("id"), rs.getString("name"))); } } } catch (SQLException e) { e.printStackTrace(); } return users; } 

    Aquí hay una forma concisa usando lambdas y JDK 8 Supplier para encajar todo en el bash externo:

      try (Connection con = DriverManager.getConnection(JDBC_URL, prop); PreparedStatement stmt = ((Supplier)() -> { try { PreparedStatement s = con.prepareStatement( "SELECT userid, name, features FROM users WHERE userid = ?"); s.setInt(1, userid); return s; } catch (SQLException e) { throw new RuntimeException(e); } }).get(); ResultSet resultSet = stmt.executeQuery()) { } 

    ¿Qué tal crear una clase contenedora adicional?

     package com.naveen.research.sql; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; public abstract class PreparedStatementWrapper implements AutoCloseable { protected PreparedStatement stat; public PreparedStatementWrapper(Connection con, String query, Object ... params) throws SQLException { this.stat = con.prepareStatement(query); this.prepareStatement(params); } protected abstract void prepareStatement(Object ... params) throws SQLException; public ResultSet executeQuery() throws SQLException { return this.stat.executeQuery(); } public int executeUpdate() throws SQLException { return this.stat.executeUpdate(); } @Override public void close() { try { this.stat.close(); } catch (SQLException e) { e.printStackTrace(); } } } 

    Luego, en la clase de llamada puede implementar el método prepareStatement como:

     try (Connection con = DriverManager.getConnection(JDBC_URL, prop); PreparedStatementWrapper stat = new PreparedStatementWrapper(con, query, new Object[] { 123L, "TEST" }) { @Override protected void prepareStatement(Object... params) throws SQLException { stat.setLong(1, Long.class.cast(params[0])); stat.setString(2, String.valueOf(params[1])); } }; ResultSet rs = stat.executeQuery();) { while (rs.next()) System.out.println(String.format("%s, %s", rs.getString(2), rs.getString(1))); } catch (SQLException e) { e.printStackTrace(); }