用于关闭JDBC对象的通用帮助器

时间:2012-08-17 15:10:57

标签: java reflection jdbc

强烈建议在使用它们时关闭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) {}}
}

现在我想通过实现一个辅助函数减少关闭对象的(重复)代码量。它将对象作为参数,并尝试使用反射调用每个对象的方法close()(如果对象确实有这样的方法)。

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
      }
    }
  }
}

finally块可以简化为:

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

这是一件好事吗?如果不是,原因是什么?是否有“更好”的方式?

4 个答案:

答案 0 :(得分:8)

就个人而言,我不会在这种情况下使用反射,当有很多好的方法可以做到这一点而不需要它。以下是您可以做的几件事。

  1. 使用Spring。 Spring有一个JdbcTemplate对象,有助于减轻Jdbc编码的冗余。样板代码隐藏在JdbcTemplate的实现中,因此您可以自由地对您的应用程序执行重要操作。
  2. 使用Java7。 Java7提供了一种新的语言结构,可以更容易地关闭实现AutoClosable接口的对象。
  3. 创建自己的库来处理样板代码。如果Spring对您的需求太重,您可以在一个基类中轻松完成所有关闭,Jdbc交互类可以从中扩展。你必须写一次,但它在很大程度上可以看不见而且不在乎。
  4. Java7方式如下所示:

    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! :)
    

答案 1 :(得分:2)

使用新的AutoCloseable功能和界面在java 7中修复了此问题。

如果您需要在java6中执行此操作,我建议使用aspectj并创建一个注释以包围close调用。

答案 2 :(得分:1)

您不需要Java 7,也不需要AOP或Spring(而且您只需要不需要反射)。你可以反转代码并使用类作为穷人的闭包(伪代码):

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 {...}
}

然后使用它你只需要这样做:

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

您从连接管理中解脱出来,您不能忘记关闭,关闭Connection应自动关闭Statement和ResultSet。

请注意,也可以使用匿名内部类,但是获得结果可能比使用此显式方法更有问题。

答案 3 :(得分:0)

关闭conn就足够了,但是首先关闭res是很好的代码风格,其次是stm。