Tengo un método para obtener usuarios de una base de datos con 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;
}
¿Cómo debo usar Java 7 try-with-resources para mejorar este código?
He intentado con el siguiente código, pero usa muchos trybloques y no mejora mucho la legibilidad . ¿Debo usar try-with-resourcesde otra manera?
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;
}
java
jdbc
java-7
try-with-resources
Jonas
fuente
fuente

try (ResultSet rs = ps.executeQuery()) {porque el objeto de declaración que lo generó cierra automáticamente un objeto ResultSetConnection,PreparedStatementyResultSettambién. No hay razón para no hacerlo realmente, ya que la prueba con recursos lo hace tan fácil y hace que nuestro código se documente más en cuanto a nuestras intenciones.Respuestas:
No hay necesidad de realizar un intento externo 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, por lo que no tiene que referirse a un método separado:fuente
Connection::setAutoCommit? Tal llamada no está permitidatryentre elcon =yps =. Al obtener una conexión de un DataSource que puede respaldarse con un grupo de conexiones, no podemos suponer cómo se configura autoCommit.DriverManager.getConnection(myConnectionURL)a un método que también establezca el indicador autoCommit y devuelva la conexión (o establecerlo en el equivalente delcreatePreparedStatementmétodo en el ejemplo anterior ...)DataSourcedonde elgetConnectionmétodo hace lo que usted dice, obtenga la conexión y configúrela según sea necesario, luego pase la conexión.Me doy cuenta de que esto fue respondido hace mucho tiempo, pero quiero sugerir un enfoque adicional que evite el doble bloqueo anidado de prueba con recursos.
fuente
createPreparedStatementno es seguro independientemente de cómo lo use. Para solucionarlo, tendría que agregar un try-catch alrededor del setInt (...), capturar cualquieraSQLException, y cuando ocurra, llame a ps.close () y vuelva a lanzar la excepción. Pero eso daría como resultado un código casi tan largo y poco elegante como el código que el OP quería mejorar.Aquí hay una manera concisa usando lambdas y JDK 8 Supplier para adaptarse a todo en el intento externo:
fuente
¿Qué pasa con la creación de una clase de contenedor adicional?
Luego, en la clase de llamada, puede implementar el método prepareStatement como:
fuente
Como han dicho otros, su código es básicamente correcto, aunque el exterior no
tryes necesario. Aquí hay algunos pensamientos más.DataSourceOtras respuestas aquí son correctas y buenas, como la respuesta aceptada por bpgergo. Pero ninguno de ellos muestra el uso
DataSource, comúnmente recomendado sobre el uso deDriverManageren Java moderno.Entonces, en aras de la integridad, aquí hay un ejemplo completo que obtiene la fecha actual del servidor de la base de datos. La base de datos utilizada aquí es Postgres . Cualquier otra base de datos funcionaría de manera similar. Reemplazaría el uso de
org.postgresql.ds.PGSimpleDataSourcecon una implementaciónDataSourceapropiada para su base de datos. Es probable que su controlador particular o grupo de conexiones proporcione una implementación si sigue esa ruta.Una
DataSourceimplementación no necesita cerrarse, porque nunca se "abre". ADataSourceno es un recurso, no está conectado a la base de datos, por lo que no contiene conexiones de red ni recursos en el servidor de la base de datos. ADataSourcees simplemente información necesaria cuando se realiza una conexión a la base de datos, con el nombre o la dirección de red del servidor de la base de datos, el nombre de usuario, la contraseña de usuario y varias opciones que desea especificar cuando finalmente se realiza una conexión. Por lo tanto, suDataSourceobjeto de implementación no va dentro de sus paréntesis de prueba con recursos.Prueba anidada con recursos
Su código hace un uso adecuado de las declaraciones anidadas de prueba con recursos.
Observe en el código de ejemplo a continuación que también usamos la sintaxis try-with-resources dos veces , una anidada dentro de la otra. El exterior
trydefine dos recursos:ConnectionyPreparedStatement. Lo internotrydefine elResultSetrecurso. Esta es una estructura de código común.Si se lanza una excepción desde el interior y no se captura allí, el
ResultSetrecurso se cerrará automáticamente (si existe, no es nulo). A continuación,PreparedStatementse cerrará y, por último,Connectionse cerrará. Los recursos se cierran automáticamente en orden inverso en el que se declararon dentro de las declaraciones de prueba con recursos.El código de ejemplo aquí es demasiado simplista. Tal como está escrito, podría ejecutarse con una única declaración de prueba con recursos. Pero en un trabajo real, es probable que esté haciendo más trabajo entre el par de
tryllamadas anidadas . Por ejemplo, puede estar extrayendo valores de su interfaz de usuario o un POJO, y luego pasarlos para cumplir con los?marcadores de posición dentro de su SQL a través de llamadas aPreparedStatement::set…métodos.Notas de sintaxis
Punto y coma final
Observe que el punto y coma que sigue a la última declaración de recursos entre paréntesis de try-with-resources es opcional. Lo incluyo en mi propio trabajo por dos razones: consistencia y parece completo, y hace que copiar y pegar una mezcla de líneas sea más fácil sin tener que preocuparme por los puntos y comas al final de la línea. Su IDE puede marcar el último punto y coma como superfluo, pero no hay nada de malo en dejarlo.
Java 9: utilice vars existentes en try-with-resources
Nuevo en Java 9 es una mejora de la sintaxis de prueba con recursos. Ahora podemos declarar y completar los recursos fuera de los paréntesis de la
trydeclaración. Todavía no he encontrado esto útil para los recursos de JDBC, pero tenlo en cuenta en tu propio trabajo.ResultSetdebería cerrarse, pero puede que noEn un mundo ideal,
ResultSetse cerraría como promete la documentación:Desafortunadamente, en el pasado, algunos controladores de JDBC no pudieron cumplir esta promesa. Como resultado, muchos programadores JDBC aprendieron a cierre de forma explícita todos sus recursos JDBC entre ellos
Connection,PreparedStatementyResultSettambién. La sintaxis moderna de prueba con recursos ha hecho que sea más fácil y con un código más compacto. Tenga en cuenta que el equipo de Java se tomó la molestia de marcarResultSetcomoAutoCloseable, y sugiero que hagamos uso de eso. El uso de una prueba con recursos alrededor de todos sus recursos JDBC hace que su código se documente más en cuanto a sus intenciones.Ejemplo de código
fuente