ActiveRecord::Rollback 在嵌套的 begin-rescue 块中的行为方式

Posted

技术标签:

【中文标题】ActiveRecord::Rollback 在嵌套的 begin-rescue 块中的行为方式【英文标题】:How ActiveRecord::Rollback behaves in nested begin-rescue blocks 【发布时间】: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' 在这种情况下不会被回滚,不是吗?

【问题讨论】:

【参考方案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

以上是关于ActiveRecord::Rollback 在嵌套的 begin-rescue 块中的行为方式的主要内容,如果未能解决你的问题,请参考以下文章

在嵌套子查询中显示配置单元分区

mongodb中在嵌套子文档的文档上面建立索引

Rails 3:取消在 after_save 上的插入

基于范围的链表的循环

嵌入式基本硬件知识

子查询(嵌套子查询)