【问题标题】:How ActiveRecord::Rollback behaves in nested begin-rescue blocksActiveRecord::Rollback 在嵌套的 begin-rescue 块中的行为方式
【发布时间】:2022-01-13 06:32:30
【问题描述】:

我有以下代码

ActiveRecord::Base.transaction do
  begin
    account.save
    # outer statement
    begin
      user.save
      # inner statement
    rescue StandardError
      raise ActiveRecord::Rollback
    end
  rescue StandardError
    raise ActiveRecord::Rollback
  end
end

如果'inner statement'出现异常,只有'user'会被回滚,对吧? 'account' 在这种情况下不会被回滚,不是吗?

【问题讨论】:

    标签: ruby-on-rails ruby activerecord


    【解决方案1】:

    在您的代码中只有一个数据库事务,它是全有或全无。回滚事务将回滚在该事务中所做的所有更改,无论您在何处发出回滚。

    您也可以嵌套事务,但请注意默认情况下事务会被挤压在一起,因此即使您在第一个事务中添加第二个事务:

    ActiveRecord::Base.transaction do
      begin
        account.save
        # outer statement
        ActiveRecord::Base.transaction do
          begin
            user.save
            # inner statement
          rescue StandardError
            raise ActiveRecord::Rollback
          end
        end
      rescue StandardError
        raise ActiveRecord::Rollback
      end
    end
    

    这仍然会导致单个事务,并且回滚将取消所有更改。

    要请求真正的子事务,需要在内部事务中添加request_new: true:

    ActiveRecord::Base.transaction do
      begin
        account.save
        # outer statement
        ActiveRecord::Base.transaction(require_new: true) do
          begin
            user.save
            # inner statement
          rescue StandardError
            raise ActiveRecord::Rollback
          end
        end
      rescue StandardError
        raise ActiveRecord::Rollback
      end
    end
    

    然而,目前唯一支持真正嵌套事务的数据库是 MS-SQL。 Rails 目前使用保存点来处理这个问题——所以不要被日志弄糊涂了。

    【讨论】:

    • 我不相信您的第二个示例基于Docs 是准确的,因为user.save 发生在父事务中,而“子”事务只是吞下了回滚。对于任何偶然发现这一点并且对任何“嵌套”交易中的回滚将回滚整个交易链的逆反应感兴趣的人。您可以找到该信息Here
    猜你喜欢
    • 1970-01-01
    • 2011-03-22
    • 1970-01-01
    • 1970-01-01
    • 2019-06-06
    • 1970-01-01
    • 2011-02-15
    • 2013-02-01
    • 2012-05-01
    相关资源
    最近更新 更多