为什么Rails忽略(伪)嵌套事务中的回滚?

时间:2014-03-14 19:01:54

标签: mysql ruby-on-rails ruby activerecord transactions

根据文档ActiveRecord::Transactions::ClassMethods,非新嵌套事务将忽略回滚。来自文档:

User.transaction do
  User.create(username: 'Kotori')
  User.transaction do
    User.create(username: 'Nemu')
    raise ActiveRecord::Rollback
  end
end

raise ActiveRecord::Rollback被忽略,因为它在子事务中(或者更确切地说,它仍在父事务中而不是它自己的事务中)。我不明白为什么两个都会忽略回滚调用?我可以看到,因为孩子的交易'这不是一个真正的交易,它不会回滚到Nemu' Nemu'阻止,但为什么它不会触发父级的回滚?子事务是否以某种方式隐藏了回滚?

换句话说,为什么似乎无法从嵌套子项中回滚父事务?

2 个答案:

答案 0 :(得分:15)

实际上,这正是嵌套交易的设计目标。我引用oracle docs:

  

嵌套事务用于为在较大事务范围内执行的操作子集提供事务保证。这样做允许您提交和中止子集   独立于较大交易的操作。

因此,常规嵌套事务中的子事务对于他或其他子女或父母(较大事务)的行为方式没有发言权,除了改变共同数据或未通过例外。

但是你可以通过传递{{1}使用rails docs中所述的sub-transaction功能,授予他(子事务)非常有限的投票机会。 }}

requires_new: true

正如文档所说:只创造' Kotori'。自从强大的Nemu'孩子选择了默默地死去。

有关嵌套交易规则oracle docs

的更多详细信息

<强>更新

为了更好地理解为什么rails User.transaction do User.create(username: 'Kotori') User.transaction(requires_new: true) do User.create(username: 'Nemu') raise ActiveRecord::Rollback end end 以这种方式工作,你需要更多地了解嵌套事务如何在数据库级别工作,我引用rails api docs

  

大多数数据库不支持真正的嵌套事务...为了   绕过这个问题,#transaction将模仿效果   嵌套事务,使用保存点:   http://dev.mysql.com/doc/refman/5.0/en/savepoint.html

好的,然后文档描述了nested transactions在上述两种情况下的行为如下:

如果是嵌套调用,#transaction的行为如下:

  • 该块将在不执行任何操作的情况下运行。块内发生的所有数据库语句都有效地附加到已经打开的数据库事务中。

  • 但是,如果设置了:requires_new,则该块将被包装在充当子事务的数据库保存点中。

我想小心,只想象

选项(1)没有 requires_new)用于您使用完全支持nested transaction的DBMS,或者您对#34感到满意;假&#34; nested transactions

的行为

选项(2)是支持nested_attributes解决方法,如果您不这样做。

答案 1 :(得分:3)

这是因为transaction do阻止了ActiveRecord::Rollback具体处理这些块中引发的transaction do异常以及Rails如何在默认情况下将嵌套transaction do块连接在一起的交互。

  1. Rails ActiveRecord::Rollback块的行为略有不同,具体取决于其中引发的异常类型:

    • transaction do块中引发transaction do例外情况时,transaction do块会拯救这些例外情况,并且不会冒泡更远。
    • 所有其他类型的例外由ActiveRecord::Rollback块拯救并重新提出,并且继续在该块之外冒泡。
  2. 默认情况下,Rails将嵌套事务“连接”在一起。这意味着只有当最外部的交易有异常泡沫时,交易才会中止。

  3. 这两种行为一起表示当在嵌套事务中引发transaction do时,它会被内部transaction do块拯救并且重新加注;外部ActiveRecord::Rollback块,因为它没有收到异常,所以成功完成。

    要强调的是,如果你提出其他而不是transaction do,它将继续通过多个transaction(requires_new: true) do块冒出来,外部事务将按预期中止。

    如其他地方所述,您可以强制Rails的嵌套事务不与transaction(joinable: false) do“加入”其父项;以及强迫父项交易不被transaction(joinable: false, requires_new: true) do的孩子加入。 recommended始终使用moment.duration(res.duration, "years").format("Y [years] M [months]"); //Input 0.08 outputs 0

    。{{3}}