一尘不染

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

mysql

根据文档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被忽略,因为它是在一个子事务(或者更确切地说,它仍然是在父事务中,而不是自己的)。我不明白为什么两者都会忽略Rollback调用?我可以看到,由于子级’transaction’并不是真正的事务,它不会回滚’Nemu’块,但是为什么它不触发父级的回滚?子事务是否以某种方式隐藏了回滚?

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


阅读 324

收藏
2020-05-17

共1个答案

一尘不染

实际上,这正是 嵌套事务 设计的目的。我引用oracle文档:

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

因此,常规 嵌套事务中 的子事务除了更改相互数据或失败失败外,没有其他关于他或其他孩子或父母( 较大事务 )的行为的发言权。

但是,你可以给他( 子事务 利用)对他命运的一个非常有限的投票机会,sub- transaction特征为在轨说明文档通过传递requires_new: true

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

正如文档所说:仅创建“ Kotori”。因为强大的“ Nemu”孩子选择默默地死。

有关 嵌套事务处理规则的 更多详细信息(oracle
docs

更新:

为了更好地理解Rails为何如此nested transactions工作,您需要更多地了解嵌套事务在数据库级别的工作方式,我引用了rails api
docs

大多数数据库不支持真正的嵌套事务…为了解决此问题,#transaction将通过使用保存点来模拟嵌套事务的效果:http
://dev.mysql.com/doc/refman/5.0/ zh /
savepoint.html

好的,然后文档描述了nested transaction上述两种情况下a的行为,如下所示:

如果发生嵌套调用,则#transaction的行为如下:

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

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

我想 小心,只想想

*如果您使用的是完全支持的DBMS, 或者您对以下选项的“假”行为感到满意,则可以使用 *option(1)不带
require_new)nested transactionsnested_attributes

如果不支持,则 option(2) 支持该savepoint解决方法。

2020-05-17