2012-08-17 11 views
6

Se recomienda encarecidamente cerrar objetos JDBC (conexiones, instrucciones, conjuntos de resultados) cuando termine de usarlos. Sin embargo, lo que produce un montón de código así:Ayudante genérico para cerrar objetos JDBC

Connection conn = null; 
Statement stm = null; 
ResultSet res = null; 
try { 
    // Obtain connection/statement, get results, whatever... 
} catch (SQLException e) { 
    // ... 
} finally { 
    if (res != null) { try { res.close(); } catch (SQLException ignore) {}} 
    if (stm != null) { try { stm.close(); } catch (SQLException ignore) {}} 
    if (conn != null) { try { conn.close(); } catch (SQLException ignore) {}} 
} 

Ahora pensé en la reducción de la cantidad de código (repetición) para el cierre de los objetos mediante la implementación de una función de ayuda. Toma los objetos como argumentos e intenta invocar el método close() de cada objeto (si el objeto tiene dicho método), utilizando la reflexión.

public void close(Object... objects) { 
    for (Object object : objects) { 
    for (Method method : object.getClass().getMethods()) { 
     if (method.getName().equals("close")) { 
     try { 
      method.invoke(object); 
     } catch (Exception e) { 
      e.printStackTrace(); 
     } 
     break; // break on the methods, go for the next object 
     } 
    } 
    } 
} 

El bloque finally puede reducirse a esto:

} finally { 
    close(res, stm, conn); 
} 

es que una buena cosa que hacer? Si no, ¿cuáles son las razones? ¿Hay una manera "mejor"?

Respuesta

8

Personalmente, no utilizaría la reflexión en este caso, cuando hay muchas maneras de hacerlo sin necesitarlo. Aquí hay un par de cosas que puedes hacer.

  1. Use Spring. Spring tiene un objeto JdbcTemplate que ayuda a aliviar la redundancia de la codificación Jdbc. El código repetitivo está oculto en la implementación de JdbcTemplate, por lo que puede hacer lo que sea importante para su aplicación.
  2. Use Java7. Java7 proporciona una nueva construcción de lenguaje que facilita el cierre de objetos que implementan la interfaz AutoClosable.
  3. Crea tu propia biblioteca para manejar el código repetitivo. Si Spring es demasiado pesado para sus necesidades, puede hacer todo el cierre fácilmente en una clase base, desde la cual pueden extenderse sus clases de interacción Jdbc. Tendrá que escribirlo una vez, pero puede, en su mayor parte, estar fuera de la vista y fuera de la mente.

forma en que el Java7 ve algo como esto:

try (
    Connection conn = getConnectionSomehow(); 
    Statement statement = getStatementFromConnSomehow(conn); 
) { 
    //use connection 
    //use statement 
} catch(SomeException ex) { 
    //do something with exception 
}//hey, check it out, conn and statement will be closed automatically! :) 
+0

En cuanto a 3., eso es lo que estoy intentando con ese método de ayuda. :) Se implementa en una clase base que todas las clases relacionadas con db extienden. La forma de Java7 necesita varios bloques de try-with-resource anidados, ¿verdad? Uno para conn, uno para stm, uno para res. – riha

+1

No, debería poder especificarlas todas en una que yo crea. Mira la edición que hice a mi ejemplo. –

+0

Por otra parte, a veces las asignaciones res y stm dependen del código en el bloque try. ¿Cómo abordarías eso? – riha

2

Este se fija en Java 7 con la nueva función AutoCloseable y la interfaz.

Si necesita hacerlo en java6, le sugiero que trabaje con aspectj y cree una anotación para envolver la llamada cercana.

1

No necesita Java 7 y no necesita AOP o Spring (y CIERTAMENTE no necesita reflexión). Usted puede simplemente invertir el código y utilizar clases como el cierre de un hombre pobre como éste (pseudocódigo):

public class DbExecutor { 
    public static void exec(DbAction action) { 
     Connection conn = null; 
     try { 
      action.consume(conn); 
     } catch (SQLException e) { 
      // ... 
     } finally { 
      if (conn != null) { try { conn.close(); } catch (SQLException ignore) {}} 
     } 

     } 
} 

public class DbAction { 
    public abstract void consume(Connection conn) throws SQLException; 

    public static class QueryOne extends DbAction { 
     public List<String> myAnswer = new ArrayList<String>(); 

     @Override 
     public abstract void consume(Connection conn) throws SQLException { 
      Statement stm = conn.prepare(...); 
      ResultSet res = stm.execute(); 
      while(res.hasNext()) { 
       myAnswer.add(...); 
      } 
      //... 
     } 
    } 

    public static class QueryTwo extends DbAction {...} 
} 

Luego de usar que usted acaba de hacer esto:

DbAction.QueryOne qAction = new DbAction.QueryOne(); 
DbExecutor.exec(qAction); 
System.out.println("found this many:" + qAction.myAnswer.size()); 

quedas libre de gestión de la conexión, no puede olvidarse de cerrar, y al cerrar la Conexión, se encargará automáticamente de cerrar la Declaración y ResultSet.

Tenga en cuenta que las clases internas anónimas también podrían utilizarse, pero obtener resultados puede ser más problemático que con este enfoque explícito.

+0

Enfoque interesante. Echaré un vistazo a cómo eso es aplicable para mí. – riha

0

Cerrar la conexión es suficiente, pero es un buen estilo de código para cerrar res primero, stm en segundo lugar.

Cuestiones relacionadas