ActiveRecord::Rollback 在嵌套开始救援块中的行为方式

How ActiveRecord::Rollback behaves in nested begin-rescue blocks

我有以下代码

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' 在那种情况下不会被回滚,不是吗?

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

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

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 目前使用保存点处理此问题 - 所以不要被日志混淆。