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 try
bloques y no mejora mucho la legibilidad . ¿Debo usar try-with-resources
de 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
,PreparedStatement
yResultSet
tambié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á permitidatry
entre 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 delcreatePreparedStatement
método en el ejemplo anterior ...)DataSource
donde elgetConnection
mé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
createPreparedStatement
no 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
try
es necesario. Aquí hay algunos pensamientos más.DataSource
Otras 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 deDriverManager
en 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.PGSimpleDataSource
con una implementaciónDataSource
apropiada para su base de datos. Es probable que su controlador particular o grupo de conexiones proporcione una implementación si sigue esa ruta.Una
DataSource
implementación no necesita cerrarse, porque nunca se "abre". ADataSource
no 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. ADataSource
es 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, suDataSource
objeto 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
try
define dos recursos:Connection
yPreparedStatement
. Lo internotry
define elResultSet
recurso. Esta es una estructura de código común.Si se lanza una excepción desde el interior y no se captura allí, el
ResultSet
recurso se cerrará automáticamente (si existe, no es nulo). A continuación,PreparedStatement
se cerrará y, por último,Connection
se 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
try
llamadas 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
try
declaración. Todavía no he encontrado esto útil para los recursos de JDBC, pero tenlo en cuenta en tu propio trabajo.ResultSet
debería cerrarse, pero puede que noEn un mundo ideal,
ResultSet
se 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
,PreparedStatement
yResultSet
tambié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 marcarResultSet
comoAutoCloseable
, 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