Как я должен использовать try-with-resources с JDBC?
У меня есть метод для получения пользователей из базы данных с помощью JDBC:
public List<User> getUser(int userId) {
String sql = "SELECT id, name FROM users WHERE id = ?";
List<User> users = new ArrayList<User>();
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;
}
Как я должен использовать Java 7 Попробуйте-с-ресурсами чтобы улучшить этот код?
Я пробовал с кодом ниже, но он использует много try
, и не улучшает читаемость сильно. Должен ли я использовать try-with-resources
другим способом?
public List<User> getUser(int userId) {
String sql = "SELECT id, name FROM users WHERE id = ?";
List<User> 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;
}
4 ответа:
Я понимаю, что ответ на этот вопрос был дан давно, но хочу предложить дополнительный подход, который позволяет избежать вложенного двойного блока try-with-resources.
public List<User> 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; }
В вашем примере нет необходимости во внешней попытке, поэтому вы можете по крайней мере спуститься с 3 до 2, а также вам не нужно закрывать
;
в конце списка ресурсов. Преимущество использования двух блоков try заключается в том, что весь ваш код присутствует заранее, поэтому вам не нужно ссылаться на отдельный метод:public List<User> getUser(int userId) { String sql = "SELECT id, username FROM users WHERE id = ?"; List<User> 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; }
Вот краткий способ использования лямбд и поставщика JDK 8, чтобы соответствовать всему во внешней попытке:
try (Connection con = DriverManager.getConnection(JDBC_URL, prop); PreparedStatement stmt = ((Supplier<PreparedStatement>)() -> { 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()) { }
Как насчет создания дополнительного класса-оболочки?
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(); } } }
Тогда в вызывающем классе можно реализовать метод prepareStatement следующим образом:
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(); }