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 块中的行为方式的主要内容,如果未能解决你的问题,请参考以下文章