example closed java jdbc connection resultset

java - closed - Dónde cerrar una conexión JDBC mientras deseo devolver el ResultSet



resultset java 8 (10)

Dónde cerrar una conexión JDBC mientras deseo devolver el ResultSet

En realidad, casi has respondido esa pregunta tú mismo. A medida que experimentaste, al cerrar la Connection se liberarán los recursos JDBC asociados a ella (al menos, así es como deberían funcionar las cosas). Por lo tanto, si desea devolver un ResultSet (volveré sobre esto más adelante), debe cerrar la conexión "más tarde". Una forma de hacerlo sería obviamente pasar una conexión a su método, algo como esto:

public ResultSet executeQuery(Connection conn, String sql, String[] getValue);

El problema es que realmente no sé cuál es tu objetivo final y por qué necesitas cosas de tan bajo nivel, así que no estoy seguro de que este sea un buen consejo. A menos que esté escribiendo un marco JDBC de bajo nivel (y, por favor, no me diga que no está haciendo esto), en realidad no recomendaría devolver un ResultSet . Por ejemplo, si desea alimentar alguna clase de negocio, devuelva algún objeto independiente de JDBC o una colección de ellos como otros han aconsejado en lugar de un ResultSet . También tenga en cuenta que un RowSet es un ResultSet por lo que si no debe usar un ResultSet entonces no debe usar un RowSet .

Personalmente, creo que deberías usar alguna clase de ayuda en lugar de reinventar la rueda. Si bien la primavera puede ser excesiva y tiene un poco de curva de aprendizaje (demasiado si no la conoces), la primavera no es la única manera de hacerlo y te sugiero encarecidamente que busques en Commons DbUtils . Más específicamente, mire a QueryRunner y especialmente a este método de query() :

public <T> T query(String sql, ResultSetHandler<T> rsh, Object... params) throws SQLException

Como puede ver, este método permite pasar un ResultSetHandler que expone un método de devolución de llamada para convertir los ResultSets en otros objetos como se describe en la respuesta de z5h y DbUtils proporciona varias implementaciones, solo elija la que se adapte a sus necesidades. También eche un vistazo a los métodos de utilidad de la clase DbUtils , por ejemplo, los diversos DbUnit.close() que le pueden resultar útiles para cerrar los recursos JDBC.

Realmente, a menos que tenga muy buenas razones para hacerlo (y me gustaría saberlo), no escriba otro marco JDBC, use una solución existente, le ahorrará algo de dolor y, lo que es más importante, algunos errores y te beneficiarás de un buen diseño comprobado. Incluso para cosas de bajo nivel, hay soluciones existentes (y simples) como vimos. Al menos, échale un vistazo.

Parece que el ResultSet se cerrará automáticamente cuando cierre la Connection . Pero quiero devolver el ResultSet y usarlo en otro método, entonces no sé dónde cerrar Connection y PreparedStatement .

public ResultSet executeQuery(String sql, String[] getValue) { Connection conn = null; PreparedStatement pstmt = null; ResultSet rs = null; try { conn = getConn(); pstmt = conn.prepareStatement(sql); if (getValue != null) { for (int i = 0; i < getValue.length; i++) { pstmt.setString(i + 1, getValue[i]); } } rs = pstmt.executeQuery(); } catch (Exception e) { e.printStackTrace(); closeAll(conn, pstmt, rs); } return rs; }

He movido closeAll(conn, pstmt, null); en el bloque catch porque descubrí que si lo pongo en el bloque finalmente rs mi rs inmediatamente antes de que regrese. Ahora, cuando quiero cerrar los rs , no puedo cerrar las conn y pstmt . ¿Hay alguna solución?


De la forma en que lo tiene ahora, la conexión nunca se cerraría, lo que causaría problemas más adelante (si no inmediatamente) para su programa y el RDBMS. Sería mejor crear una clase Java para mantener los campos del ResultSet y devolverlos. El ResultSet está vinculado a la conexión, por lo que no es posible devolverlo y cerrar la conexión.


La forma más limpia es usar CachedRowSetImpl . Pero en MySQL 5.x + hay algunos errores al seleccionar columnas por nombre o etiqueta.

Para usar con MySQL use esta versión: https://.com/a/17399059/1978096


No puede usar ResultSet después de haber cerrado Connection y / o PreparedStatement . Por lo tanto, debe pasar un objeto para realizar una devolución de llamada en este método.

Toda la limpieza debe hacerse en bloques finally .

Reescribelo asi

public ResultSet executeQuery( String sql, String[] getValue, CallbackObj cbObj ) throws SQLException { final Connection conn = getConn( ); try { final PreparedStatement pstmt = conn.prepareStatement(sql); try { if (getValue != null) { for (int i = 0; i < getValue.length; i++) { pstmt.setString(i + 1, getValue[i]); } } final ResultSet rs = pstmt.executeQuery(); try { cbObj.processResultSet( rs ); } finally { // You may want to handle SQLException // declared by close rs.close( ); } } finally { // You may want to handle SQLException // declared by close pstmt.close( ); } } finally { // You may want to handle SQLException // declared by close conn.close( ); } }


Nunca debe pasar ResultSet (o Statement o Connection ) al público fuera del bloque de método donde se deben adquirir y cerrar para evitar fugas de recursos. Una práctica común es simplemente asignar el ResultSet a una List<Data> donde Data es solo un objeto javabean que representa los datos de interés.

Aquí hay un ejemplo básico:

public class Data { private Long id; private String name; private Integer value; // Add/generate public getters + setters. }

Y aquí hay un ejemplo básico de cómo manejarlo correctamente:

public List<Data> list() throws SQLException { Connection connection = null; PreparedStatement statement = null; ResultSet resultSet = null; List<Data> list = new ArrayList<Data>(); try { connection = database.getConnection(); statement = connection.prepareStatement("SELECT id, name, value FROM data"); resultSet = statement.executeQuery(); while (resultSet.next()) { Data data = new Data(); data.setId(resultSet.getLong("id")); data.setName(resultSet.getString("name")); data.setValue(resultSet.getInt("value")); list.add(data); } } finally { if (resultSet != null) try { resultSet.close(); } catch (SQLException logOrIgnore) {} if (statement != null) try { statement.close(); } catch (SQLException logOrIgnore) {} if (connection != null) try { connection.close(); } catch (SQLException logOrIgnore) {} } return list; }

Puedes usarlo de la siguiente manera:

List<Data> list = dataDAO.list();

Para obtener más información sobre las mejores prácticas con JDBC, también puede encontrar útil este artículo de inicio básico .


Puede llamar a ResultSet.getStatement para recuperar la Statement y a Statement.getConnection para recuperar la Connection .

A partir de estos, puede escribir un método de utilidad closeResultSet que cerrará los 3 por usted, solo con el ResultSet .


Realmente no deberías manejar con JDBC en el nivel inferior. En su lugar, use un marco como spring , que se encargará de todas las operaciones de close() para usted.


Te recomiendo que hagas algo más como esto:

public List<Map> executeQuery(Connection connection, String sql) throws SQLException { List<Map> rows = new ArrayList<Map>(); PreparedStatement stmt = null; ResultSet rs = null; try { pstmt = conn.prepareStatement(sql); rs = stmt.execute(); int numColumns = rs.getMetaData().getColumnCount(); while (rs.next()) { Map<String, Object> row = new LinkedHashMap<String, Object>(); for (int i = 0; i < numColumns; ++i) { String column = rs.getColumnName(i+1); Object value = rs.getObject(i+1); row.put(column, value); } rows.add(row); } } finally { close(rs); close(stmt); } return rows; } public static void close(Statement s) { try { if (s != null) { s.close(); } } catch (SQLException e) { e.printStackTrace(); } } public static void close(ResultSet rs) { try { if (rs != null) { rs.close(); } } catch (SQLException e) { e.printStackTrace(); } }


Una forma limpia de codificar esto es pasar un objeto que tiene un método de devolución de llamada que toma un conjunto de resultados.

Su otro método crea el objeto con el método de devolución de llamada con su código de manejo resultSet, y lo pasa al método que ejecuta el SQL.

De esa manera, su código SQL y DB se queda donde pertenece, su lógica de manejo de conjunto de resultados está más cerca de donde usa los datos y su código SQL se limpia cuando debería.

interface ResultSetCallBack{ void handleResultSet(ResultSet r); } void executeQuery(..., ResultSetCallBack cb){ //get resultSet r ... cb.handleResultSet(r); //close connection } void printReport(){ executeQuery(..., new ResultSetCallBack(){ public void handleResultSet(ResultSet r) { //do stuff with r here } }); }


Use CachedRowSet para mantener la información después de desconectar

Connection con = ... ResultSet rs = ... CachedRowSet rowset = new CachedRowSetImpl(); rowset.populate(rs); con.close()