6

强烈建议在使用完 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

4 回答 4

8

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

  1. 使用弹簧。Spring 有一个JdbcTemplate对象可以帮助减轻 Jdbc 编码的冗余。样板代码隐藏在 的实现中JdbcTemplate,因此您可以自由地为您的应用程序做重要的事情。
  2. 使用Java7。Java7 提供了一种新的语言结构,可以更轻松地关闭实现AutoClosable接口的对象。
  3. 创建您自己的库来处理样板代码。如果 Spring 对您的需求来说太重了,您可以在一个基类中轻松完成所有关闭,您的 Jdbc 交互类可以从该基类扩展。你将不得不写一次,但在大多数情况下,它可能是看不见的,也可能是心不在焉的。

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! :)
于 2012-08-17T15:15:04.823 回答
2

这在 java 7 中通过新的 AutoCloseable 特性和接口得到了修复。

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

于 2012-08-17T15:15:15.450 回答
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());

你从连接管理中解脱出来,你不能忘记关闭,关闭连接应该自动处理关闭语句和结果集。

请注意,也可以使用匿名内部类,但获取结果可能比使用这种显式方法更成问题。

于 2012-08-17T18:41:49.420 回答
0

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

于 2012-08-22T13:28:02.007 回答