【问题标题】:Detect exception in AutoCloseable close()在 AutoCloseable close() 中检测异常
【发布时间】:2011-12-04 15:19:05
【问题描述】:

我想建立一个自定义的AutoCloseable 类,这样我就可以转这个:

try {
    begin();
    doThings();
    commit();
} finally {
    if (transactionIsActive()) rollback();
}

变得更容易

try (Transaction t = begin()) { // too bad I have to store it in t though I don't use it
    doThings();
}

Transaction 将是此处的AutoCloseable,在close() 中它将酌情提交或回滚事务。

但要使其正常工作,我需要在 Transaction.close() 中检测 try 块内是否发生异常或正常完成。这可能吗?

如果它需要从新异常中解析堆栈跟踪,那没关系。更简单的编程将值得带来微小的性能损失。

【问题讨论】:

  • 我相信 try-with-resources 专门用于处理资源的设置和清理本身引发异常的情况,从而导致可怕的finally { try { if (resource != null) { resource.close() … 块。它不应该是 Python 的上下文管理器。
  • @Inerdial 也许,但如果可能的话(这个问题希望能显示出来)我不反对为了这样的目的滥用它们。
  • “特别”是指我浏览了规范,据我所知,所发生的只是生成了与设置/清理块相对应的字节码,only i> 您可以将其作为初始化程序和AutoCloseable.close 的实现挂钩的点,它们都不接受异常参数。

标签: java exception-handling transactions java-7


【解决方案1】:

我能想到的最接近的仍然需要手动将交易成功标记为块的最后一条语句:

class Transaction implements AutoCloseable {
    private boolean rollback = true;

    public void success() {
        rollback = false;
    }

    public void close() {
        if (rollback) doRollback();
        else doCommit();
        // …
    }
}

class Main {
    public static void main(String[] args) {
        try (Transaction t = new Transaction()) {
            doThings();
            t.success();
        }
    }
}

【讨论】:

    【解决方案2】:

    虽然我的代码与你的不同,但我也有类似的需求,即自动提交(大多数)事务并在错误时回滚。

    大多数情况下,我的代码都散布着自动回滚的简单查询,如下所示:

    try(Transaction t : database.beginTransaction()) {
      return t.selectUnique(Employee.class, "id=?", 200);
    }  // implicit rollback here
    

    有些数据库不喜欢像这样回滚查询,所以我通过区分“写”和“读”事务来解决这个问题。如果是读事务,close() 将提交,否则回滚。它还将检查您在创建只读事务时没有执行任何写入操作。所以现在我可以写了:

    try(Transaction t : database.beginReadOnlyTransaction()) {
      return t.selectUnique(Employee.class, "id=?", 200);
    }  // implicit commit here
    

    写事务最后还是需要自己调用commit,但这只是少数情况。

    我知道这不是你想要的,但也许它仍然有用。

    【讨论】:

      【解决方案3】:

      我能得到的最接近的方法是显式调用 commit(),并假设任何退出事务块的代码都应该回滚。这与其他语言的事务一致。虽然您可能会忘记调用 commit()(就像我经常做的那样),但至少这部分代码很可能会被测试。而且,不可能忘记回滚异常,这不太可能具有测试覆盖率。

      这类似于millimoose设置标志的想法:

      try (Transaction t = new Transaction()) {
          doThings();
          t.success();
      }
      

      除了你只是使用活动状态作为标志。相同数量的代码,不需要新的标志。这假设任何未显式调用 commit() 的事务都应回滚,从而产生如下代码:

      try (Transaction t = new Transaction()) {
          doThings();
          t.commit(); // marks the transaction as successful...
      }
      
      class Transaction implements AutoCloseable {
          public void close() {
              if (isActive())
                  doRollback();
          }
      
          ...
      }
      

      我仍然无法相信核心语言中没有更简洁的解决方案。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2018-12-08
        • 2014-12-15
        • 2014-05-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-05-06
        相关资源
        最近更新 更多