Java 7自动资源管理JDBC(try with resources语句)

Java 7自动资源管理JDBC(try with resources语句),java,jdbc,java-7,try-with-resources,Java,Jdbc,Java 7,Try With Resources,如何将创建/接收连接、查询数据库以及可能处理结果的常见JDBC习惯用法与Java7的自动资源管理(try with resources语句)集成在一起?() 在Java 7之前,通常的模式是这样的: Connection con = null; PreparedStatement prep = null; try{ con = getConnection(); prep = prep.prepareStatement("Update ..."); ... con

如何将创建/接收连接、查询数据库以及可能处理结果的常见JDBC习惯用法与Java7的自动资源管理(try with resources语句)集成在一起?()

在Java 7之前,通常的模式是这样的:

Connection con = null;
PreparedStatement prep = null;

try{
    con = getConnection();
    prep = prep.prepareStatement("Update ...");
    ...
    con.commit();
}
catch (SQLException e){
    con.rollback(); 
    throw e;
}
finally{
    if (prep != null)
        prep.close();
    if (con != null)
        con.close();
}
使用Java 7,您可以:

try(Connection con = getConnection(); PreparedStatement prep = con.prepareConnection("Update ..."){

   ...
   con.commit();
}
这将关闭
连接
PreparedStatement
,但是回滚又如何呢?我无法添加包含回滚的catch子句,因为连接仅在try块中可用


您仍然在try块之外定义连接吗?这里的最佳实践是什么,特别是在使用连接池的情况下?

IMO,在try-catch之外声明connection和PreparedStatement是这种情况下可用的最佳方法

try(Connection con = getConnection()) {
   try (PreparedStatement prep = con.prepareConnection("Update ...")) {
       //prep.doSomething();
       //...
       //etc
       con.commit();
   } catch (SQLException e) {
       //any other actions necessary on failure
       con.rollback();
       //consider a re-throw, throwing a wrapping exception, etc
   }
}
根据,您可以将try with resources块与常规try块组合。在IMO中,上述示例捕获了正确的逻辑,即:

  • 如果没有问题,尝试关闭PreparedStatement
  • 如果内部块出错,(无论是什么),回滚当前事务
  • 无论发生什么情况,都尝试关闭连接
  • 如果关闭连接时出错,则无法回滚事务(因为这是连接上的一个方法,现在处于不确定状态),因此不要尝试

在Java6和更早的版本中,我会使用一组三重嵌套的try块(外部try finally、中间try catch、内部try finally)来实现这一点。ARM语法确实使这一点更加简洁。

如果您想在事务中使用池连接,您应该以以下方式使用它:

try (Connection conn = source.getConnection()) {
        conn.setAutoCommit(false);
        SQLException savedException = null;
        try {
            // Do things with connection in transaction here...
            conn.commit();
        } catch (SQLException ex) {
            savedException = ex;
            conn.rollback();
        } finally {
            conn.setAutoCommit(true);
            if(savedException != null) {
                throw savedException;
            }
        }
    } catch (SQLException ex1) {
        throw new DataManagerException(ex1);
    }
此示例代码处理自动提交值的设置


请注意,如果
conn.rollback()
引发另一个异常,则使用
savedException
会保存异常。这样,finally block将抛出“right”异常。

在这些情况下,我不会使用自动关闭。正如该术语已经指出的,它只是用于关闭资源。顺便说一句:将连接放置在
try…
块之外没有帮助,因为在try块之后无法回滚,因为连接已经关闭。可能重复@Raedwald:不,这不是重复。这里是关于con.rollback()。您的解决方案在
SQLException
上回滚,但在
RuntimeException
Error
(等等)上不回滚,这是一个问题<代码>最后,基于代码的代码可以做到这一点。“您不应捕获
错误
”规则不会使回滚成为发生错误时的可选操作。@PiotrFindeisen:如果需要,请捕获
可丢弃的
,而不是
SQLException
。Finally不是回滚事务的正确位置,因为Finally块无论是否出错都会被触发。当一切正常时,你肯定不想倒退!是,除非您在离开try块之前提交(在成功的情况下)。当然,在这种情况下,您有commit-then-rollbacknothing,这并不理想,但在某些情况下,它肯定比缺少rollback要好。顺便说一句,我建议编辑答案,因为这是一个很好的复制粘贴代码,它应该处理所有异常。@PiotrFindeisen在try块中提交并在finally块中回滚是不正确的。即使一切正常,也会导致额外的往返,这是一个显著的低效。最后,块用于确保对象图处于一致状态(关闭连接、语句、流等),而不采取只有在发生错误时才会发生的操作(回滚)。@seanrelly如果PreparedStatement的自动关闭引发异常,该怎么办?这不会触发不必要的回滚吗?看起来,我们可以简化一些事情。为什么我们需要如此详细的异常处理?我们可以只使用:
try(Connection conn=source.getConnection()){conn.setAutoCommit(false);try{…;conn.commit();}catch(SQLException ex){conn.rollback();throw ex;}最后{conn.setAutoCommit(true);}
有时您希望在不同的情况下抛出特定的DataManagerException(或其子类之一)异常。另请注意
savedException
变量。