根据语言规范,将在执行 catch 子句之前关闭连接(http://docs.oracle.com/javase/specs/jls/se7/html/jls-14.html#jls-14.20.3.2) .
一个可能的解决方案是嵌套 try-with-resources 语句:
try (java.sql.Connection con = createConnection())
{
con.setAutoCommit(false);
try (Statement stm = con.createStatement())
{
stm.execute(someQuery); // causes SQLException
}
catch(SQLException ex)
{
con.rollback();
con.setAutoCommit(true);
throw ex;
}
con.commit();
con.setAutoCommit(true);
}
希望这能说明这一点。如果您打算在生产代码中使用它,这应该会有所改进。
例如,如果您正在使用连接池,那么您必须在获得连接时返回连接,因此 con.setAutoCommit(true); 应该在 finally 子句中完成。这意味着外部的 try-with-resources 应该是传统的 try-catch-finally。
编辑 (2018)
我仍然看到人们对此发表评论,所以我想我会给它一个 2018 年的回复。我不再使用Java了,主要是使用Scala、Clojure和Kotlin,并且这段代码没有经过测试,所以请把这当作另一个例子。但是,由于 Java 有 lambda,我认为以下方法要好得多。我在这些其他语言的生产代码中做了类似的事情。
在这种方法中,有一个 inTransaction 函数来处理所有讨厌的事务。但是用法很简单。
public class Foo {
interface ConnectionProvider {
Connection get() throws SQLException;
}
public static <A> A doInTransation(ConnectionProvider connectionProvider, Function<Connection, A> f) throws SQLException {
Connection connection = null;
A returnValue;
boolean initialAutocommit = false;
try {
connection = connectionProvider.get();
initialAutocommit = connection.getAutoCommit();
connection.setAutoCommit(false);
returnValue = f.apply(connection);
connection.commit();
return returnValue;
} catch (Throwable throwable) {
// You may not want to handle all throwables, but you should with most, e.g.
// Scala has examples: https://github.com/scala/scala/blob/v2.9.3/src/library/scala/util/control/NonFatal.scala#L1
if (connection != null) {
connection.rollback();
}
throw throwable;
} finally {
if (connection != null) {
try {
if(initialAutocommit){
connection.setAutoCommit(true);
}
connection.close();
} catch (Throwable e) {
// Use your own logger here. And again, maybe not catch throwable,
// but then again, you should never throw from a finally ;)
StringWriter out = new StringWriter();
e.printStackTrace(new PrintWriter(out));
System.err.println("Could not close connection " + out.toString());
}
}
}
}
public static void main(String[] args) throws SQLException {
DataSource ds = null;
// Usage example:
doInTransation(ds::getConnection, (Connection c) -> {
// Do whatever you want in a transaction
return 1;
});
}
}
我希望有一些经过实战考验的库可以为您做这些事情,至少在这些其他语言中是这样。
我看到有一些关于自动提交和连接池的评论。上面的例子应该不知道连接来自哪里,一个池与否,即只有当它是初始值时才将它设置回true。因此,如果从池中它是错误的,则不应触摸它。
关于资源尝试的最后一句话。我不认为这是一个很好的抽象,所以我会在更复杂的场景中小心使用它。