【问题标题】:Java 7 Automatic Resource Management JDBC (try-with-resources statement)Java 7 自动资源管理 JDBC(try-with-resources 语句)
【发布时间】:2012-03-04 20:00:19
【问题描述】:

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

在 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();
}

这将关闭ConnectionPreparedStatement,但是回滚呢?我无法添加包含回滚的 catch 子句,因为该连接仅在 try 块中可用。

您仍然在 try 块之外定义连接吗?这里的最佳做法是什么,尤其是在使用连接池的情况下?

【问题讨论】:

  • 我不会在这些情况下使用自动关闭。正如该术语已经指出的那样,它仅用于关闭资源。顺便说一句:将连接放在 try... 块之外将无济于事,因为由于连接已关闭,您无法在 try 块之后回滚。
  • @Raedwald:不,这不是重复的。这里是关于 con.rollback() 的。

标签: java jdbc java-7 try-with-resources


【解决方案1】:
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
   }
}

根据oracle documentation,您可以将 try-with-resources 块与常规 try 块结合使用。 IMO,上面的例子捕捉到了正确的逻辑,就是:

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

在 java 6 和更早版本中,我会使用一组三重嵌套的 try 块(外部 try-finally、中间 try-catch、内部 try-finally)来执行此操作。 ARM 语法确实使这个更简洁。

【讨论】:

  • 您的解决方案在SQLException 上回滚,但在RuntimeExceptionError (等)上没有,这有点问题。基于finally 的代码可以做到这一点。规则“你不能捕获Error”不会使回滚成为一个可选操作,以防出错。
  • @PiotrFindeisen:如果你想这样,那就抓住Throwable 而不是SQLException。 finally 不是回滚事务的正确位置,因为无论是否出现问题都会触发 finally 块。当一切正常时,您绝对不想回滚!
  • 是的,除非您在离开 try 块之前提交(在成功的情况下)。当然,在这种情况下,你有 commit-then-rollback-nothing,这并不理想,但在某些情况下,它肯定比缺少回滚要好。顺便说一句,我建议编辑答案,因为它是非常好的复制粘贴代码,它应该处理所有异常。
  • @PiotrFindeisen 在 try 块中提交并在 finally 块中回滚是不正确的。即使一切正常,它也会导致额外的往返,这是一个显着的低效率。 finally 块用于确保对象图处于一致状态(关闭连接、语句、流等)采取仅在发生错误时才发生的操作(回滚)。
  • @SeanReilly 如果自动关闭 PreparedStatement 抛出异常怎么办?这不会触发不必要的回滚吗?
【解决方案2】:

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

【讨论】:

    【解决方案3】:

    如果你想在事务中使用池连接,你应该这样使用它:

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

    此示例代码处理设置自动提交值。

    注意,使用savedException 确实会保存异常,以防conn.rollback() 抛出另一个异常。这样,finally 块就会抛出“正确”的异常。

    【讨论】:

    • 看起来,我们可以稍微简化一下。为什么我们需要如此冗长的异常处理?我们可以使用:try (Connection conn = source.getConnection()) { conn.setAutoCommit(false); try {...; conn.commit(); } catch (SQLException ex) { conn.rollback(); throw ex;} finally { conn.setAutoCommit(true);}}
    • 有时您希望在不同情况下抛出特定的 DataManagerException(或其子类之一)异常。另请注意savedException 变量。
    猜你喜欢
    • 1970-01-01
    • 2020-08-10
    • 2020-10-23
    • 1970-01-01
    • 2022-08-15
    • 1970-01-01
    • 2017-11-18
    • 2013-09-12
    • 2013-06-30
    相关资源
    最近更新 更多