【问题标题】:try with resources: detect exceptions?尝试资源:检测异常?
【发布时间】:2017-12-17 06:21:21
【问题描述】:

这是一个示例代码:

     try ( PgConnection conn = application.adminDb.getConnection() ) {
         // execute SQL statements here (and other possible statements)
     }

我希望 AutoCloseable PgConnection 类在创建时开始新事务,并在关闭时提交或回滚。如果在 try 块中没有抛出异常,它应该 COMMIT,否则它应该 ROLLBACK。

我真正需要的是一个上下文管理器 a'la Python。有可能以某种方式做到这一点吗?理论上,在调用 PgConnection.close() 时,JVM 已经知道是否存在异常。就是不知道怎么说。

当然我可以这样说:

try ( PgConnection conn = application.adminDb.getConnection()) {

    conn.begin();
    try {
      // execute SQL statements here
      conn.commit();
    } catch (Exception e) {
      conn.rollback();
      throw e;
    }

}

但我需要这样做一百次,所以我正在寻找一个“噪音较小”的选项。

更新

我当然可以这样做:

public interface PgTransactionWrapper {
    public void run(PgConnection connection) throws SQLException;
}

然后把这个放到PgConnection中:

   public void runInTransaction(PgTransactionWrapper wrapper) throws SQLException {
       int trId = begin();
       try {
           wrapper.run(this);
           commit(trId);
       } catch (SQLException e) {
           rollback(trId);
           throw e;
       }
   }

这就是它的使用方法:

     try ( PgConnection conn = application.adminDb.getConnection() ) {
         conn.runInTransaction(new PgTransactionWrapper() {

            @Override
            public void run(PgConnection connection) throws SQLException {
                // SQL statements here

            }
        });
     }

但我认为情况更糟。确实实现了自动提交/回滚,但付出的代价是更多的语法噪音,并且引入了一个额外的问题:事务包装器除了 SQLException 不能抛出任何东西。

我想念的是 AutoCloseable.close(Exception excOrNull) 的一个简单参数,但我想这是语言的限制,无法做到。 :-(

更新 2:只是为了澄清问题:我不想抑制或转换异常。我只想在没有异常的时候提交,在有异常的时候回滚。但除此之外,我不想从内部处理它们——它们应该原封不动地传播给调用者。

【问题讨论】:

  • 一个围绕 PgConnection 的可自动关闭的包装类,它在关闭时根据 on 状态自动提交/回滚(默认:不成功,除非另有明确设置)?
  • 可以,但是如何设置状态呢?如果我想在 catch 子句中设置状态,那么我必须为每个单独的用例创建一个额外的 try/catch 块。我认为这会比我的“显式”示例更糟糕,因为它仍然会增加语法噪音,并用隐式的替换显式提交/回滚。
  • 您的最后一个示例很嘈杂,因为您没有使用 lambda 表达式,并且存在异常问题,因为您仍在使用检查异常而不是运行时异常(尽管如果try 块在 runInTransaction()) 内。如果您使用 lamda 表达式和运行时异常,就会变得非常清楚。如果您使用 AOP 允许的声明式事务,使用 Spring 或 CDI/EJB 等框架,则更加清晰。
  • 我对spring aop不熟悉。它可以处理嵌套事务吗?
  • Btw SQLException 不是运行时异常。我怎么不能用呢?

标签: java try-catch


【解决方案1】:

这是我对扩展您的代码的想法。你怎么看?至少您不必捕获异常并在所有地方处理它们以提交/回滚您的事务

公共接口 SQLStatementExecutor
{
    无效运行(PgConnection 连接)抛出 SQLException;
}

公共类 PgConnectionWrapper 实现 Closeable
{

    私有 PgConnection 连接;
    私人布尔状态=假;

    公共 PgConnectionWrapper()
    {
        连接 = 获得连接();
    }

    私人 PgConnection 获得连接()
    {
        返回 application.adminDb.getConnection();
    }

    公共无效runInTransaction(SQLStatementExecutor执行器)抛出SQLException
    {
        尝试 {
            executor.run(连接);
            状态=真;
        }
        捕捉(SQLException e){
            状态=假;
            扔 e;
        }
    }

    公共 PgConnection getConnection()
    {
        返回连接;
    }

    @Override public void close() 抛出 IOException
    {
        如果(状态){
            连接.commit();
        }
        别的 {
            连接.回滚();
        }

        连接.close();
    }
}

公共类TestMain
{

    公共静态 void main(String[] args) 抛出 IOException
    {
        尝试(PgConnectionWrapper connectionWrapper = new PgConnectionWrapper()){
            connectionWrapper.runInTransaction(新 SQLStatementExecutor()
            {
                @Override public void run(PgConnection connection) 抛出 SQLException
                {
                    // SQL 语句,但没有提交或回滚
                }
            });
        }
    }
}

【讨论】:

  • 我认为除了SQLException之外我仍然无法从代码中抛出任何东西。例如,我不能在核心代码块中混合使用 MongoDb 和 PostgreSQL 操作,因为 MongoDb 操作会抛出 MongoQueryException。这是一个真实的例子——我实际上需要支持嵌套事务,并根据嵌套 PostgreSQL 事务的成功/失败执行不同的 MongoDb 操作。关于语法噪音,简单的显式开始/提交/回滚似乎更清晰,更容易理解它的工作原理,并且不限制可以抛出的异常。
  • 您是否考虑过将数据库特定的异常包装到您的类型的异常中并处理它们?我很确定上面的示例代码也可以通过将 ConnectionWrapper 传递给 SQLStatementExecutor 而不是 PgConnection 来重写以处理嵌套事务。
  • 所以你还建议我捕获所有可能的异常,并将它们转换为其他东西。然后它们不能被调用者处理。但我不想处理这些异常,也不想将它们抑制/转换为其他任何东西。我只想提交或回滚,仅此而已。我想我正在寻找一种在 Java 中根本不存在的语言结构。 :-(
猜你喜欢
  • 2020-03-30
  • 1970-01-01
  • 2014-06-06
  • 2010-09-28
  • 2011-12-21
  • 1970-01-01
  • 1970-01-01
  • 2017-01-03
  • 1970-01-01
相关资源
最近更新 更多